import os, sys
sys.path.insert(0, os.path.expanduser('~') + '/lib/python')
from mylib.mygl import *


def mygl_internal_get_parallaxmap_funcs(uvtrans):
    string = ""
    if "parallaxmap" in uvtrans:
        string += """
        vec2 parallaxmap(sampler2D heightmap, vec2 texcoords, float scale, bool flip){
            float fDepth = 0.0;
            vec2 vHalfOffset = vec2(0.0,0.0);
            int i = 0;
            vec2 eyevec = vec2(dot(vVertex,t),dot(vVertex,b));
            if (flip){while (i < 3){
                fDepth= fDepth+(    texture2D(heightmap,texcoords+vHalfOffset).r) *0.5;
                vHalfOffset=normalize(eyevec).xy*fDepth*scale;
                i+=1;
            }}
            else     {while (i < 3){
                fDepth=(fDepth+(1.0-texture2D(heightmap,texcoords+vHalfOffset).r))*0.5;
                vHalfOffset=normalize(eyevec).xy*fDepth*scale;
                i+=1;
            }}
            return vHalfOffset;
        }"""
    if "parallax_occulsionmap" in uvtrans:
        string += """
        vec2 parallax_occulsionmap(sampler2D heightmap, vec2 texcoords, float scale, bool flip, int linear_search_steps, int binary_search_steps) {
            scale = scale/10.0;
            vec2 eyevec = vec2(dot(vVertex,t),dot(vVertex,b));
            vec2 ds=eyevec.xy*scale,offset;
            float depth_step=1.0/float(linear_search_steps),size=depth_step,depth=0.0,best_depth=1.0;
            if (flip) {
                for (int i=0; i<linear_search_steps-1; i++){
                    depth += size;vec4 t = texture2D(heightmap,texcoords+(ds*depth));
                    if (best_depth>0.996) {
                        if (depth>=t.r){
                            best_depth=depth;
                }}}
                depth = best_depth;
                for (int i=0; i<binary_search_steps; i++){
                    size *= 0.5;vec4 t = texture2D(heightmap,texcoords+(ds*depth));
                    if (depth>=t.r) {
                        best_depth=depth;depth-=2.0*size;
                    }
                    depth += size;
                }}
            else {
                for (int i=0; i<linear_search_steps-1; i++){
                    depth += size;vec4 t = 1.0-texture2D(heightmap,texcoords+(ds*depth));
                    if (best_depth>0.996) {
                        if (depth>=t.r){best_depth=depth;
                }}}
                depth = best_depth;
                for (int i=0; i<binary_search_steps; i++){
                    size *= 0.5;vec4 t = 1.0-texture2D(heightmap,texcoords+(ds*depth));
                    if (depth>=t.r) {
                        best_depth=depth;depth-=2.0*size;
                    }
                    depth += size;
                }}
            offset = ds*best_depth;
            return offset;//return vec3(offset.x,offset.y,best_depth);
        }"""
        string += """
        vec2 parallax_occulsionmap2(sampler2D heightmap, vec2 texture_size, vec2 texcoords, float height_scalar, bool flipdepth, int steps_guide) {
            vec3 vertex_vector = normalize(vVertex);
            vec2 texture_space_vec = vec2(dot(vertex_vector,normalize(t)),dot(vertex_vector,normalize(b)));
            vec3 eyevec = normalize(vec3(texture_space_vec,-(1.0-length(texture_space_vec))));

            int steps = 10;//mix(steps_guide*2, steps_guide, dot(normal,E))

            float last_height = texture2D(heightmap,texcoords).r;
            if (last_height==1.0) { return vec2(0.0,0.0); }
            if (flipdepth) { discard; }
            float new_height = 0.0;

            vec3 volume_position = vec3(0.0,0.0,1.0);
            if (eyevec.z>=-0.01) {discard;return vec2(0.0,0.0);}
            float x_sc_length=0.0, y_sc_length=0.0, minlength=0.0;
            while (volume_position.z>new_height) {
                if (eyevec.x<0.0) { x_sc_length=(( ceil(volume_position.x)-1.0)-volume_position.x)/dot(eyevec,vec3(1.0,0.0,0.0)); }
                else              { x_sc_length=((floor(volume_position.x)+1.0)-volume_position.x)/dot(eyevec,vec3(1.0,0.0,0.0)); }
                if (eyevec.y<0.0) { y_sc_length=(( ceil(volume_position.y)-1.0)-volume_position.y)/dot(eyevec,vec3(0.0,1.0,0.0)); }
                else              { y_sc_length=((floor(volume_position.y)+1.0)-volume_position.y)/dot(eyevec,vec3(0.0,1.0,0.0)); }
                minlength = min(x_sc_length,y_sc_length);
                volume_position.xy += minlength*eyevec.xy;
                volume_position.z += height_scalar*minlength*eyevec.z;
                new_height = texture2D(heightmap,texcoords+(volume_position.xy/texture_size)).r;
                //if (length(volume_position.xz)>2.0) { discard;return vec2(0.0,0.0); }
            }

            vec2 offset = volume_position.xy/texture_size;
            return offset;
        }"""
    return string


def mygl_internal_get_subsurface_absorb_funcs(renderequation):
    string = ""
    if "absorb" in renderequation:
        if "absorb2_" in renderequation:
            string += """
            float absorb2_point(vec2 coord,float alpha,float near,float far) {
                float depth1=texture2D(tex2D_1,coord).r;float depth2=texture2D(tex2D_2,coord).r;
                depth1=depth_from_depthbuffer(depth1,near,far);depth2=depth_from_depthbuffer(depth2,near,far);
                float length = depth2-depth1;
                return clamp(pow(10.0,-alpha*length),0.0,1.0);
            }
            float absorb2_proj(vec4 depth_coord,float alpha,float near,float far) {
                float coord = depth_from_depthbuffer(depth_coord.p/depth_coord.q,near,far);
                float depth1=texture2DProj(tex2D_1,depth_coord).r;float depth2=texture2DProj(tex2D_2,depth_coord).r;
                depth1=depth_from_depthbuffer(depth1,near,far);depth2=depth_from_depthbuffer(depth2,near,far);
                float diff1=abs(coord-depth1);float diff2=abs(coord-depth2);
                float length = 0.0;
                if (diff2<diff1) { length = coord-depth1; }
                return clamp(pow(10.0,-alpha*length),0.0,1.0);
            }"""
        if "absorb4_" in renderequation:
            string += """
            float absorb4_point(vec2 coord,float alpha,float near,float far) {
                float depth1=texture2D(tex2D_1,coord).r;float depth2=texture2D(tex2D_2,coord).r;
                float depth3=texture2D(tex2D_3,coord).r;float depth4=texture2D(tex2D_4,coord).r;
                depth1=depth_from_depthbuffer(depth1,near,far);depth2=depth_from_depthbuffer(depth2,near,far);
                depth3=depth_from_depthbuffer(depth3,near,far);depth4=depth_from_depthbuffer(depth4,near,far);
                float length = (depth2-depth1)+(depth4-depth3);
                return clamp(pow(10.0,-alpha*length),0.0,1.0);
            }
            float absorb4_proj(vec4 projcoord,float alpha,float near,float far) {
                float coord = depth_from_depthbuffer(depth_coord.p/depth_coord.q,near,far);
                float depth1=texture2DProj(tex2D_1,depth_coord).r;float depth2=texture2DProj(tex2D_2,depth_coord).r;
                float depth3=texture2DProj(tex2D_3,depth_coord).r;float depth4=texture2DProj(tex2D_4,depth_coord).r;
                depth1=depth_from_depthbuffer(depth1,near,far);depth2=depth_from_depthbuffer(depth2,near,far);
                depth3=depth_from_depthbuffer(depth3,near,far);depth4=depth_from_depthbuffer(depth4,near,far);
                float diff1=abs(coord-depth1);float diff2=abs(coord-depth2);float diff3=abs(coord-depth3);float diff4=abs(coord-depth4);
                float length = 0.0;
                if ((diff2<diff4)&&(diff2<diff3)&&(diff2<diff1)) { length = coord-depth1; }
                if ((diff3<diff4)&&(diff3<diff2)&&(diff3<diff1)) { length = (depth2-depth1); }
                if ((diff4<diff3)&&(diff4<diff2)&&(diff4<diff1)) { length = (depth4-depth3)+(depth2-depth1); }
                return clamp(pow(10.0,-alpha*length),0.0,1.0);
            }"""
        if "absorb6_" in renderequation:
            string += """
            float absorb6_point(vec2 coord,float alpha,float near,float far) {
                float depth1=texture2D(tex2D_1,coord).r;float depth2=texture2D(tex2D_2,coord).r;
                float depth3=texture2D(tex2D_3,coord).r;float depth4=texture2D(tex2D_4,coord).r;
                float depth5=texture2D(tex2D_5,coord).r;float depth6=texture2D(tex2D_6,coord).r;
                depth1=depth_from_depthbuffer(depth1,near,far);depth2=depth_from_depthbuffer(depth2,near,far);
                depth3=depth_from_depthbuffer(depth3,near,far);depth4=depth_from_depthbuffer(depth4,near,far);
                depth5=depth_from_depthbuffer(depth5,near,far);depth6=depth_from_depthbuffer(depth6,near,far);
                float length = (depth2-depth1)+(depth4-depth3)+(depth6-depth5);
                return clamp(pow(10.0,-alpha*length),0.0,1.0);
            }
            float absorb6_proj(vec4 depth_coord,float alpha,float near,float far) {
                float coord = depth_from_depthbuffer(depth_coord.p/depth_coord.q,near,far);
                float depth1=texture2DProj(tex2D_1,depth_coord).r;float depth2=texture2DProj(tex2D_2,depth_coord).r;
                float depth3=texture2DProj(tex2D_3,depth_coord).r;float depth4=texture2DProj(tex2D_4,depth_coord).r;
                float depth5=texture2DProj(tex2D_5,depth_coord).r;float depth6=texture2DProj(tex2D_6,depth_coord).r;
                depth1=depth_from_depthbuffer(depth1,near,far);depth2=depth_from_depthbuffer(depth2,near,far);
                depth3=depth_from_depthbuffer(depth3,near,far);depth4=depth_from_depthbuffer(depth4,near,far);
                depth5=depth_from_depthbuffer(depth5,near,far);depth6=depth_from_depthbuffer(depth6,near,far);
                float diff1=abs(coord-depth1);float diff2=abs(coord-depth2);float diff3=abs(coord-depth3);float diff4=abs(coord-depth4);float diff5=abs(coord-depth5);float diff6=abs(coord-depth6);
                float length = 0.0;
                if ((diff2<diff6)&&(diff2<diff5)&&(diff2<diff4)&&(diff2<diff3)&&(diff2<diff1)) { length = coord-depth1; }
                if ((diff3<diff6)&&(diff3<diff5)&&(diff3<diff4)&&(diff3<diff2)&&(diff3<diff1)) { length = depth2-depth1; }
                if ((diff4<diff6)&&(diff4<diff5)&&(diff4<diff3)&&(diff4<diff2)&&(diff4<diff1)) { length = (depth4-depth3)+(depth2-depth1); }
                if ((diff5<diff6)&&(diff5<diff4)&&(diff5<diff3)&&(diff5<diff2)&&(diff5<diff1)) { length = (depth4-depth3)+(depth2-depth1); }
                if ((diff6<diff5)&&(diff6<diff4)&&(diff6<diff3)&&(diff6<diff2)&&(diff6<diff1)) { length = (depth6-depth5)+(depth4-depth3)+(depth2-depth1); }
                return clamp(pow(10.0,-alpha*length),0.0,1.0);
            }"""
        if "absorb8_" in renderequation:
            string += """
            float absorb8_point(vec2 coord,float alpha,float near,float far) {
                float depth1=texture2D(tex2D_1,coord).r;float depth2=texture2D(tex2D_2,coord).r;
                float depth3=texture2D(tex2D_3,coord).r;float depth4=texture2D(tex2D_4,coord).r;
                float depth5=texture2D(tex2D_5,coord).r;float depth6=texture2D(tex2D_6,coord).r;
                float depth7=texture2D(tex2D_7,coord).r;float depth8=texture2D(tex2D_8,coord).r;
                depth1=depth_from_depthbuffer(depth1,near,far);depth2=depth_from_depthbuffer(depth2,near,far);
                depth3=depth_from_depthbuffer(depth3,near,far);depth4=depth_from_depthbuffer(depth4,near,far);
                depth5=depth_from_depthbuffer(depth5,near,far);depth6=depth_from_depthbuffer(depth6,near,far);
                depth7=depth_from_depthbuffer(depth7,near,far);depth8=depth_from_depthbuffer(depth8,near,far);
                float length = (depth2-depth1)+(depth4-depth3)+(depth6-depth5)+(depth8-depth7);
                return clamp(pow(10.0,-alpha*length),0.0,1.0);
            }
            float absorb8_proj(vec4 depth_coord,float alpha,float near,float far) {
                float coord = depth_from_depthbuffer(depth_coord.p/depth_coord.q,near,far);
                float depth1=texture2DProj(tex2D_1,depth_coord).r;float depth2=texture2DProj(tex2D_2,depth_coord).r;
                float depth3=texture2DProj(tex2D_3,depth_coord).r;float depth4=texture2DProj(tex2D_4,depth_coord).r;
                float depth5=texture2DProj(tex2D_5,depth_coord).r;float depth6=texture2DProj(tex2D_6,depth_coord).r;
                float depth7=texture2DProj(tex2D_7,depth_coord).r;float depth8=texture2DProj(tex2D_8,depth_coord).r;
                depth1=depth_from_depthbuffer(depth1,near,far);depth2=depth_from_depthbuffer(depth2,near,far);
                depth3=depth_from_depthbuffer(depth3,near,far);depth4=depth_from_depthbuffer(depth4,near,far);
                depth5=depth_from_depthbuffer(depth5,near,far);depth6=depth_from_depthbuffer(depth6,near,far);
                depth7=depth_from_depthbuffer(depth7,near,far);depth8=depth_from_depthbuffer(depth8,near,far);
                float diff1=abs(coord-depth1);float diff2=abs(coord-depth2);float diff3=abs(coord-depth3);float diff4=abs(coord-depth4);float diff5=abs(coord-depth5);float diff6=abs(coord-depth6);float diff7=abs(coord-depth7);float diff8=abs(coord-depth8);
                float length = 0.0;
                if ((diff2<diff8)&&(diff2<diff7)&&(diff2<diff6)&&(diff2<diff5)&&(diff2<diff4)&&(diff2<diff3)&&(diff2<diff1)) { length = coord-depth1; }
                if ((diff3<diff8)&&(diff3<diff7)&&(diff3<diff6)&&(diff3<diff5)&&(diff3<diff4)&&(diff3<diff2)&&(diff3<diff1)) { length = depth2-depth1; }
                if ((diff4<diff8)&&(diff4<diff7)&&(diff4<diff6)&&(diff4<diff5)&&(diff4<diff3)&&(diff4<diff2)&&(diff4<diff1)) { length = (depth4-depth3)+(depth2-depth1); }
                if ((diff5<diff8)&&(diff5<diff7)&&(diff5<diff6)&&(diff5<diff4)&&(diff5<diff3)&&(diff5<diff2)&&(diff5<diff1)) { length = (depth4-depth3)+(depth2-depth1); }
                if ((diff6<diff8)&&(diff6<diff7)&&(diff6<diff5)&&(diff6<diff4)&&(diff6<diff3)&&(diff6<diff2)&&(diff6<diff1)) { length = (depth6-depth5)+(depth4-depth3)+(depth2-depth1); }
                if ((diff7<diff8)&&(diff6<diff7)&&(diff5<diff5)&&(diff4<diff4)&&(diff3<diff3)&&(diff2<diff2)&&(diff1<diff1)) { length = (depth6-depth5)+(depth4-depth3)+(depth2-depth1); }
                if ((diff8<diff7)&&(diff8<diff6)&&(diff8<diff5)&&(diff8<diff4)&&(diff8<diff3)&&(diff8<diff2)&&(diff8<diff1)) { length = (depth8-depth7)+(depth6-depth5)+(depth4-depth3)+(depth2-depth1); }
                return clamp(pow(10.0,-alpha*length),0.0,1.0);
            }"""
    return string


def mygl_internal_get_shadow_funcs(renderequation):
    string = ""
    # if   "df_shadowed" in renderequation:
    #        #http://www.gamedev.net/community/forums/topic.asp?topic_id=571102
    #        string += """
    #        const float bias = 0.0001;
    #        float gllibinternal_df(sampler2D shadtex,vec2 texel_size,vec2 uv,float depth) {
    #            vec2 distance = vec2(0.0,0.0);
    #            float ks = 1.0;
    #            float count = 0.0;
    #            for (float i=-ks;i<=ks;i++) {
    #                for (float j=-ks;j<=ks;j++) {
    #                    vec2 sample = vec2(i,j)*texel_size;
    #
    #                    //Shadow test
    #                    float test = min(bias,depth-texture2D(shadtex,uv+sample).x)/bias;
    #
    #                    //This texel's contribution to distance calculation:
    #                    distance += sample*test;
    #                    count += test;
    #                }
    #            }
    #            if (count<0.1) { return 1.0; } //No shadowed texels, maximum distance:
    #            return length(distance/count) / (ks*sum(texel_size)/2.0); //Return length of the average sample vector
    #        }
    #        float df_shadowed(sampler2D shadtex,vec2 texture_size,vec4 depth_coord) {
    #            vec2 proj = depth_coord.xy/depth_coord.w;
    #            float depth = depth_coord.z/depth_coord.w;
    #
    #            vec2 texel_size = 1.0/texture_size;
    #
    #            float tl = gllibinternal_df(shadtex,texel_size,proj,                                depth);
    #            float tr = gllibinternal_df(shadtex,texel_size,proj+vec2(texel_size.x,         0.0),depth);
    #            float bl = gllibinternal_df(shadtex,texel_size,proj+vec2(         0.0,texel_size.y),depth);
    #            float br = gllibinternal_df(shadtex,texel_size,proj+vec2(texel_size.x,texel_size.y),depth);
    #
    #            vec2 f = fract(proj.xy*texture_size);
    #            float shadow = mix(mix(tl,tr,f.x),
    #                               mix(bl,br,f.x),f.y);
    #            shadow = smoothstep(0.0,1.0,(shadow-0.5)/(1.0-0.5));
    #
    #            return shadow;
    #        }"""
    if "shadowed" in renderequation:  #elif
        string += """
        float gllibinternal_shadproj(sampler2D shadtex,vec4 depth_coord,float default_color,bool flip_coords) {
            float value = default_color;
            if (depth_coord.w>0.0) {
                vec2 testcoord = depth_coord.xy/depth_coord.w;
                if (testcoord.x>=0.0) { if (testcoord.x<=1.0) { if (testcoord.y>=0.0) { if (testcoord.y<=1.0) {
                    if (!flip_coords) { value = (clamp(depth_coord.z/depth_coord.w,0.0,1.0)<=texture2DProj(shadtex,depth_coord).r) ? 1.0:0.0; }
                    else {
                        value = (clamp(depth_coord.z/depth_coord.w,0.0,1.0)<=texture2D(shadtex,vec2(depth_coord.x/depth_coord.w,1.0-(depth_coord.y/depth_coord.w))).r) ? 1.0:0.0;
                    }
                }}}}
            }
            return value;
        }
        float shadowed(sampler2D shadtex,vec4 depth_coord)                                      { return gllibinternal_shadproj(shadtex,depth_coord,1.0,false); }
        float shadowed(sampler2D shadtex,vec4 depth_coord,bool flip_coords)                     { return gllibinternal_shadproj(shadtex,depth_coord,1.0,flip_coords); }
        float shadowed(sampler2D shadtex,vec4 depth_coord,float default_color)                  { return gllibinternal_shadproj(shadtex,depth_coord,default_color,false); }
        float shadowed(sampler2D shadtex,vec4 depth_coord,float default_color,bool flip_coords) { return gllibinternal_shadproj(shadtex,depth_coord,default_color,flip_coords); }"""
#        if "shadowed_pcf" in renderequation:
#            string += """
#            float shadowed_pcf(sampler2D shadtex,float radius,int kernel=3,float default_color=1.0,bool flip_coords) {
#                float shadow=0.0,value;
#                vec2 testcoord;
#                vec4 coord = vec4(0.0);
#                float depth = ProjCoord.p/ProjCoord.q;
#                for (int v=-kernel; v<=kernel; v++) { for (int u=-kernel; u<=kernel; u++) {
#                    value = default_color;
#                    coord = ProjCoord+(radius*vec4(u,v,0.0,0.0));
#                    testcoord = coord.xy/coord.w;
#                    if (testcoord.x>=0.0) { if (testcoord.x<=1.0) { if (testcoord.y>=0.0) { if (testcoord.y<=1.0) {
#                        value = (depth<=texture2DProj(shadtex,coord).x) ? 1.0:0.0;
#                    }}}}
#                    shadow += value;
#                }}
#                int dim = (2*kernel)+1;
#                return shadow/float(dim*dim);
#            }
#            //float shadowed_pcf(sampler2D shadtex,float radius) {return shadowed_pcf(shadtex,radius,3,1.0);}
#            //float shadowed_pcf(sampler2D shadtex,float radius,int kernel) {return shadowed_pcf(shadtex,radius,kernel,1.0);}
#            //float shadowed_pcf(sampler2D shadtex,float radius,float default_color) {return shadowed_pcf(shadtex,radius,3,default_color);}"""
#        if "shadowed_edges" in renderequation:
#            string += """
#            float ShadProjEdges(sampler2D shadtex,vec4 coord,float default_color) {
#                float value = default_color;
#                value = (coord.p/coord.q<=texture2DProj(shadtex,coord).r) ? 1.0:0.0;
#                return value;
#            }
#        float shadowed_edges(sampler2D shadtex) { return ShadProjEdges(shadtex,ProjCoord,1.0); }
#        float shadowed_edges(sampler2D shadtex,float default_color) { return ShadProjEdges(shadtex,ProjCoord,default_color); }"""
#        if "shadowed_pcf_edges" in renderequation:
#        #NO OPTIONAL ARGS--THIS WON'T WORK
#            string += """
#            float shadowed_pcf_edges(sampler2D shadtex,float radius,int kernel=3) {
#                float shadow=0.0,value;
#                vec2 testcoord;
#                vec4 coord = vec4(0.0);
#                float depth = ProjCoord.p/ProjCoord.q;
#                for (int v=-kernel; v<=kernel; v++) {
#                    for (int u=-kernel; u<=kernel; u++) {
#                        value = default_color;
#                        coord = ProjCoord+(radius*vec4(u,v,0.0,0.0));
#                        value = (depth<=texture2DProj(shadtex,coord).x) ? 1.0:0.0;
#                        shadow += value;
#                    }
#                }
#                int dim = (2*kernel)+1;
#                return shadow/float(dim*dim);
#            }"""
#http://www.sin.cvut.cz/~micro/viz/doc/shadows_en.pdf
    return string


def mygl_internal_get_cubemap_funcs(renderequation):
    string = ""
    if "cubemap_" in renderequation:
        # distance = (distance-near) / (far-near);
        #        shaddepth = 1.0/( shaddepth*-2.0 + 1.0 - ((far+near)/(near-far)) );

        string += """
        float cubemap_depthtest(samplerCube cubetex, vec3 center, float near, float far) {
            vec3 lightdir = vec4(transform_matrix*vertex).xyz - center;

            float distance = max(max(abs(lightdir.x),abs(lightdir.y)),abs(lightdir.z));
            distance = ((far+near)/(far-near)) + (1.0/distance)*((-2.0*far*near)/(far-near));
            distance = (distance+1.0)/2.0;

            float shaddepth = textureCube(cubetex,normalize(lightdir)).r;

            if (distance>shaddepth) { return 0.0; }
            else                    { return 1.0; }
        }"""
        if "cubemap_normal" in renderequation:
            string += """
        vec3 cubemap_normal() {
            return vec4(vec4(normalize(mat3(t,b,n)*vec3(0.0,0.0,1.0)),1.0)*gl_ModelViewMatrix).xyz;
        }
        vec3 cubemap_normal_from_normalmap(vec3 normalmapsample) {
            return vec4(vec4(normal_from_normalmap(normalmapsample),1.0)*gl_ModelViewMatrix).xyz;
        }"""
        if "cubemap_ref" in renderequation:
            string += """
        vec4 cubemap_reflect_sample(samplerCube cubetex, vec3 cubemapnormal) {
            vec3 dir = vec4(vertex-gl_ModelViewMatrixInverse[3]).xyz;
            vec3 reflectvec = vec4(transform_matrix*vec4(reflect(dir,cubemapnormal),1.0)).xyz;
            return textureCube(cubetex,reflectvec);
        }
        vec4 cubemap_refract_sample(samplerCube cubetex, vec3 cubemapnormal, float eta) {
            vec3 dir = vec4(vertex-gl_ModelViewMatrixInverse[3]).xyz;
            vec3 refract_normal = cubemapnormal;
            float refract_eta = eta;
            if (dot(dir,cubemapnormal)>0.0) { eta = 1.0/eta; refract_normal *= -1.0; }
            vec3 refractvec = vec4(transform_matrix*vec4(refract(dir,refract_normal,refract_eta),1.0)).xyz;
            return textureCube(cubetex,refractvec);
        }"""
    return string


def mygl_internal_get_light_funcs(renderequation):
    string = ""
    if "fresnel_coefficient" in renderequation:
        # http://www.cse.ohio-state.edu/~kerwin/refraction.html
        string += """
        vec2 fresnel_coefficient(float n1,float n2) {
            float eta = n2 / n1;

            float cos_theta1 = dot(E,normal);
            float cos_theta2 = sqrt(1.0-(eta*eta*( 1.0-cos_theta1*cos_theta1)));

            vec3 reflection = E - 2.0*cos_theta1*normal;
            vec3 refraction = eta*E + (cos_theta2 - eta*cos_theta1)*normal;

            float fresnel_rs = (n2*cos_theta1 - n1*cos_theta2) / (n2*cos_theta1 + n1*cos_theta2);
            float fresnel_rp = (n1*cos_theta1 - n2*cos_theta2) / (n1*cos_theta1 + n2*cos_theta2);

            float reflectance = (fresnel_rs*fresnel_rs + fresnel_rp*fresnel_rp) / 2.0;
            reflectance = reflectance>1.0 ? 0.0:reflectance;
            float transmittance = 1.0-reflectance;

            return vec2(reflectance,transmittance);
        }"""
    if "light_" in renderequation:
        string += """
        float abs_diffuse_coefficient(gl_LightSourceParameters light) {
            vec3 l = normalize(light.position.xyz-vVertex);
            return abs(dot(normal,l));
        }
        float abs_specular_coefficient_ph(gl_LightSourceParameters light) {
            vec3 l = normalize(light.position.xyz-vVertex);
            vec3 reflected = reflect(-l,normal);
            return abs(dot(reflected,E));
        }

        float diffuse_coefficient(gl_LightSourceParameters light) {
            vec3 l = normalize(light.position.xyz-vVertex);
            return max(dot(normal,l),0.0);
        }
        float specular_coefficient_ph(gl_LightSourceParameters light) {
            return max(dot(reflect(normalize(vVertex-light.position.xyz),normal),E),0.0);
        }
        float specular_coefficient_bl(gl_LightSourceParameters light) {
            return max(dot(normal,-normalize(light.halfVector.xyz)),0.0);
        }
        float specular_coefficient_gs(gl_LightSourceParameters light) {
            return exp(-pow(specular_coefficient_bl(light)/0.9,2.0));
        }
        float specular_coefficient_bk(gl_LightSourceParameters light, float m) {
            float NdotH = dot(normal,normalize(light.halfVector.xyz));
            float exponent = -pow( tan(acos(NdotH))/m, 2.0 );
            return exp(exponent)/(4.0*m*m*pow(NdotH,4.0));
        }
        vec4 light_ambient(gl_LightSourceParameters light) {
            return light.ambient;
        }
        vec4 light_diffuse(gl_LightSourceParameters light) {
            float coeff = diffuse_coefficient(light);
            return light.diffuse*coeff;
        }
        vec4 light_specular_ph(gl_LightSourceParameters light) { float coeff = specular_coefficient_ph(light); float powexp = pow(coeff,clamp(gl_FrontMaterial.shininess,0.001,128.0)); return light.specular*powexp; }
        vec4 light_specular_bl(gl_LightSourceParameters light) { float coeff = specular_coefficient_bl(light); float powexp = pow(coeff,clamp(gl_FrontMaterial.shininess,0.001,128.0)); return light.specular*powexp; }
        vec4 light_specular_gs(gl_LightSourceParameters light) { float coeff = specular_coefficient_gs(light); float powexp = pow(coeff,clamp(gl_FrontMaterial.shininess,0.001,128.0)); return light.specular*powexp; }
        vec4 light_specular_bk(gl_LightSourceParameters light, float m) { float coeff = specular_coefficient_bk(light,m); return light.specular*coeff; }
        float light_att(gl_LightSourceParameters light) {
            vec3 l = light.position.xyz-vVertex.xyz;
            float dist = length(l);
            float cons = light.constantAttenuation;
            float linear = light.linearAttenuation * dist;
            float quad = light.quadraticAttenuation * dist * dist;
            float denom = cons + linear + quad;
            return clamp(1.0/denom,0.0,1.0);
        }"""
        if "abs_light" in renderequation:
            string += """
        vec4 abs_light_diffuse(gl_LightSourceParameters light) {
            float coeff = abs_diffuse_coefficient(light);
            return light.diffuse*coeff;
        }
        vec4 abs_light_specular_ph(gl_LightSourceParameters light) {
            float coeff = abs_specular_coefficient_ph(light);
            float powexp = pow(coeff,clamp(gl_FrontMaterial.shininess,0.001,128.0));
            return light.specular*powexp;
        }"""
        if "light_spot" in renderequation:
            string += """
        float light_spot(gl_LightSourceParameters light) {
	    float dot_LD = dot( normalize(vVertex-light.position.xyz), normalize(light.spotDirection) );
	    float coeff = 1.0 - (1.0-dot_LD)/(1.0-light.spotCosCutoff);
	    return (dot_LD>light.spotCosCutoff) ? pow(coeff,light.spotExponent) : 0.0;

        }"""
    return string


def mygl_internal_get_caustic_func(renderequation):
    string = ""
    if "add_caustics" in renderequation:
        string += """
        vec3 add_caustics(sampler2D caustictex,vec4 projcoord) {
            vec3 value = vec3(0.0);
            vec2 testcoord = projcoord.xy/projcoord.w;
            if (testcoord.x>=0.0) { if (testcoord.x<=1.0) { if (testcoord.y>=0.0) { if (testcoord.y<=1.0) { value = texture2DProj(caustictex,projcoord).rgb; }}}}
            return value;
        }"""
    return string


def mygl_internal_get_rotation_funcs(section):
    string = ""
    if "rotation_matrix_" in section:
        string += """
        mat3 rotation_matrix_x(float angle) {
            float cos_angle=cos(angle);float sin_angle=sin(angle);
            return mat3(vec3(1.0,0.0,       0.0      ),
                        vec3(0.0,cos_angle,-sin_angle),
                        vec3(0.0,sin_angle, cos_angle));
        }
        mat3 rotation_matrix_y(float angle) {
            float cos_angle=cos(angle);float sin_angle=sin(angle);
            return mat3(vec3( cos_angle,0.0,sin_angle),
                        vec3( 0.0,      1.0,0.0      ),
                        vec3(-sin_angle,0.0,cos_angle));
        }
        mat3 rotation_matrix_z(float angle) {
            float cos_angle=cos(angle);float sin_angle=sin(angle);
            return mat3(vec3(cos_angle,-sin_angle,0.0),
                        vec3(sin_angle, cos_angle,0.0),
                        vec3(0.0,       0.0,      1.0));
        }
        mat3 rotation_matrix_arbitrary(vec3 vec,float angle) {
            float c=cos(angle);float s=sin(angle);
            float C = 1.0 - c;
            float  xs = vec.x* s; float  ys = vec.y* s; float  zs = vec.z* s;
            float  xC = vec.x* C; float  yC = vec.y* C; float  zC = vec.z* C;
            float xyC = vec.x*yC; float yzC = vec.y*zC; float zxC = vec.z*xC;
            return mat3(vec3(vec.x*xC+c,    xyC-zs,    zxC+ys),
                        vec3(    xyC+zs,vec.y*yC+c,    yzC-xs),
                        vec3(    zxC-ys,    yzC+xs,vec.z*zC+c));
        }"""
    return string


def mygl_internal_get_texture_funcs(section):
    string = ""
    # if "texture2Dcf" in section:
    #        string += """
    #        vec4 texture2Dcf(sampler2D texture, vec2 size, vec2 coord) {
    #            vec2 pixel_coord = coord*size;
    #            vec2 pixel_intersect_loc = round(pixel_coord);
    #            vec2 p00 = pixel_intersect_loc - vec2(0.5);
    #            vec2 p11 = p00 + vec2(1.0);
    #            vec2 part = pixel_intersect_loc - pixel_coord;
    #            part = -part+0.5;
    #            p00 /= size;
    #            p11 /= size;
    #            vec4 h00 = texture2D(texture,vec2(p00.x,p00.y));
    #            vec4 h10 = texture2D(texture,vec2(p11.x,p00.y));
    #            vec4 h01 = texture2D(texture,vec2(p00.x,p11.y));
    #            vec4 h11 = texture2D(texture,vec2(p11.x,p11.y));
    #            vec2 height = -2.0*pow(part,3.0)+3.0*pow(part,2.0);
    #            vec4 x0 = h00 + height.x*(h10-h00);
    #            vec4 x1 = h01 + height.x*(h11-h01);
    #            vec4 result = x0 + height.y*(x1-x0);
    #            return result;
    #        }"""
    if "texture2Dbc" in section:
        string += """
        vec4 texture2Dbc(sampler2D texture, vec2 size, vec2 coord, float a) {
            return ;
        }"""
    return string


def mygl_internal_shader(type, args):
    renderequation = ""
    uservars = ""
    prevertex = ""
    verttrans = ""
    postvertex = ""
    uvtrans = ""
    extfuncvert = ""
    extfuncfrag = ""
    max1Dtextures = 0
    max2Dtextures = 8
    max3Dtextures = 0
    maxcubetextures = 0
    maxrendertargets = 1
    if type == MYGL_BLANK:  # Passed
        renderequation = "color = vec4(1.0);"
        max2Dtextures = 0
    elif type == MYGL_PHONG:  # Passed
        renderequation = "vec3 lvec=light_vector(0);color.rgb=((ambient_color*light_ambient(0))+(((diffuse_color*light_diffuse(0,diffuse_coefficient(normal,lvec)))+(specular_color*light_specular(0,specular_coefficient_ph(normal,lvec))))*light_att(0))).rgb;"
        max2Dtextures = 0
    # elif type == MYGL_BLINN: #Not Passed.  When passed, update Documentation.doc
    #        renderequation = "vec3 lvec=light_vector(0);color.rgb=((ambient_color*light_ambient(0))+(((diffuse_color*light_diffuse(0,diffuse_coefficient(normal,lvec)))+(specular_color*light_specular(0,specular_coefficient_bl(normal))))*light_att(0))).rgb;"
    elif type == MYGL_TEXTURE:  # Passed
        renderequation = "color = texture2D(tex2D_1,uv);"
        max2Dtextures = 1
    elif type == MYGL_VIEW_DEPTHBUFFER:  # Passed
        renderequation = "color = vec4(1.0-vec3(float(gl_FragCoord.z-gl_DepthRange.near)/float(gl_DepthRange.far-gl_DepthRange.near)),1.0);"
        max2Dtextures = 0
    elif type == MYGL_VIEW_NORMALS:  # Close enough
        renderequation = "color = vec4((n.xy+1.0)/2.0,n.z,1.0);"
        max2Dtextures = 0
    elif type == MYGL_POSITION_NORMAL_MAP:  # Right--I think...
        uservars = "uniform vec2 size;uniform bool stage1;uniform int fbotype;uniform int type;uniform int render_pass;"
        renderequation = """
        if (  (render_pass>1)  &&  (gl_FragCoord.z<=texture2D(tex2D_1,gl_FragCoord.xy*(1.0/size)).r)  ) { discard; }
        else {
            if (fbotype==1) {
                if (stage1) {
                    float depth = (gl_FragCoord.z-gl_DepthRange.near)/float(gl_DepthRange.far-gl_DepthRange.near);
                    if      (type==1) {                color.r = depth; } //receiver
                    else if (type==2) { color.g = 0.5; color.b = depth; } //refractive
                    else if (type==3) { color.g = 1.0; color.b = depth; } //reflective
                }
                else {
                    color.rgb = vec3(realnorm*0.5)+vec3(0.5);
                }
            }
            else if (fbotype==2) {
                float depth = (gl_FragCoord.z-gl_DepthRange.near)/float(gl_DepthRange.far-gl_DepthRange.near);
                if          (type==1) {                color.r = depth; } //receiver
                else {
                    if      (type==2) { color.g = 0.5; color.b = depth; } //refractive
                    else if (type==3) { color.g = 1.0; color.b = depth; } //reflective
                    color2.rgb = vec3(realnorm*0.5)+vec3(0.5); //in if/else because receiver normals are irrelevant
                }
            }
        }"""
        max2Dtextures = 1
        maxrendertargets = 2
    elif type == MYGL_CAUSTIC_MAP:  # reflection backwards needs work!  And ray trace!
        uservars = """
        uniform float eta;
        uniform float grid_sc;
        uniform vec2 size;
        uniform vec3 light_splat_color;
        varying float continuing;
        varying vec2 coord;
        uniform mat4 modelmatrix;"""
        extfuncvert = """
        vec2 get_coord(vec2 v, vec2 size) { return vec2(v.xy*vec2(vec2(1.0)-(vec2(1.0)/size)))+vec2(vec2(0.5)/size); }"""
        verttrans = """
        coord = get_coord(vertex.xy,size);
        vertex.xy = vec2(grid_sc*(vertex.xy-vec2(0.5)))+vec2(0.5);
        vec4 sample = texture2D(tex2D_2,coord);

        continuing = 1.0;
        if (sample.g==0.0) { continuing=0.0; }
        else {
            float delta_depth = sample.r-sample.b;
            vertex.xy = (coord*size)+vec2(0.5);
            vec3 normal = normalize(texture2D(tex2D_3,coord).rgb-vec3(0.5));
            normal = (modelmatrix*vec4(normal,0.0)).xyz;
            normal.z = -normal.z;
            vec3 lightdir = vec3(0.0,0.0,1.0);//normalize(vec3(vec2(0.5)-coord,sample.b));
            if (abs(sample.g-0.5)<0.05) {
                if (delta_depth<0.0) { continuing = 0.0; }
                else {
                    float refract_eta = eta;
                    if (dot(lightdir,normal)>0.0) { refract_eta = 1.0/refract_eta; normal *= -1.0; }
                    vec3 refractedray = refract(lightdir,normal,refract_eta);
                    vertex.xy += size*(delta_depth/refractedray.z)*refractedray.xy;
                }
            }
            else if (delta_depth>0.0) {
                vec3 reflectedray = reflect(lightdir,normal);
                vertex.xy += size*(abs(delta_depth)/reflectedray.z)*reflectedray.xy;
                //coord = get_coord(vertex.xy,size);
                //sample = texture2D(tex2D_1,coord);
                //delta_depth = sample.r-sample.b;
                if (reflectedray.z<0.0) { //reflecting back
                    if (delta_depth>0.0) { continuing = 0.0; }
                    else                 { continuing = 1.0; }
                }
                else { //reflecting away
                    if (delta_depth<0.0) { continuing = 0.0; }
                    else                 { continuing = 1.0; }
                }

            }
            else { continuing = 0.0; }
        }"""
        renderequation = """
        if (continuing==0.0) { discard; }
        vec2 v_rot = normalize(vertex.zw);
        vec4 l_uv = vec4(0.0,0.0,gl_PointCoord.xy);
        l_uv.zw-=vec2(0.5,0.5);l_uv.x=l_uv.z*v_rot.x;l_uv.y=l_uv.w*v_rot.x;l_uv.x-=l_uv.w*v_rot.y;l_uv.y+=l_uv.z*v_rot.y;
        color = texture2D(tex2D_1,l_uv.xy+vec2(0.5,0.5));
        color.rgb *= light_splat_color;"""
        max2Dtextures = 3
    elif type == MYGL_DEPTH_PEEL:
        uservars = "uniform vec2 size;uniform bool stage1;"
        renderequation = """
        color.rgb = vec3(1.0);
        if (!stage1) { if (gl_FragCoord.z<=texture2D(tex2D_2,gl_FragCoord.xy*(1.0/size)).r) { discard; } }"""
        max2Dtextures = 2
        maxrendertargets = 1
    elif type == MYGL_DEPTH_PEEL_TYPES:
        uservars = "uniform vec2 size;uniform bool stage1;uniform vec3 colortype;"
        renderequation = """
        color2.rgb = colortype;
        if (!stage1) { if (gl_FragCoord.z<=texture2D(tex2D_1,gl_FragCoord.xy*(1.0/size)).r) { discard; } }"""
        max2Dtextures = 1
        maxrendertargets = 2
    elif type == MYGL_DEPTH_PEEL_TRANSPARENT:
        uservars = "uniform vec2 size;uniform bool stage1;uniform bool volume;uniform float near;uniform float far;"
        renderequation = """
        color.rgb = vec3(1.0);
        if (!stage1) {
            if (!volume) { }//gl_FragDepth = texture2D(tex2D_1,gl_FragCoord.xy*(1.0/size)).r; }
            else if (gl_FragCoord.z<=texture2D(tex2D_1,gl_FragCoord.xy*(1.0/size)).r) { discard; }
        }"""
        max2Dtextures = 1
        maxrendertargets = 1
    #    elif type == MYGL_DEPTH_PEEL_SINGLE_PASS:
    #        uservars = "uniform vec2 size;"
    #        extfuncfrag = """
    #        void move_2_to_1(float depth) {                               gl_FragData[0].r=depth;gl_FragDepth=depth; }
    #        void move_3_to_2(float depth) { move_2_to_1(gl_FragData[1].r);gl_FragData[1].r=depth;gl_FragDepth=depth; }
    #        void move_4_to_3(float depth) { move_3_to_2(gl_FragData[2].r);gl_FragData[2].r=depth;gl_FragDepth=depth; }
    #        void move_5_to_4(float depth) { move_4_to_3(gl_FragData[3].r);gl_FragData[3].r=depth;gl_FragDepth=depth; }
    #        void move_6_to_5(float depth) { move_5_to_4(gl_FragData[4].r);gl_FragData[4].r=depth;gl_FragDepth=depth; }
    #        void move_7_to_6(float depth) { move_6_to_5(gl_FragData[5].r);gl_FragData[5].r=depth;gl_FragDepth=depth; }
    #        void move_8_to_7(float depth) { move_7_to_6(gl_FragData[6].r);gl_FragData[6].r=depth;gl_FragDepth=depth; }
    #        void move_9_to_8(float depth) { move_8_to_7(gl_FragData[7].r);gl_FragData[7].r=depth;gl_FragDepth=depth; }"""
    #        renderequation = """
    #        float depth = gl_FragCoord.z;
    #        float d1 = gl_FragData[0].r;
    #        float d2 = gl_FragData[1].r;
    #        float d3 = gl_FragData[2].r;
    #        float d4 = gl_FragData[3].r;
    #        float d5 = gl_FragData[4].r;
    #        float d6 = gl_FragData[5].r;
    #        float d7 = gl_FragData[6].r;
    #        float d8 = gl_FragData[7].r;
    #        if      (depth>d8) { discard; }
    #        else if (depth>d7) { move_2_to_1(gl_FragCoord.z); }
    #        else if (depth>d6) { move_3_to_2(gl_FragCoord.z); }
    #        else if (depth>d5) { move_4_to_3(gl_FragCoord.z); }
    #        else if (depth>d4) { move_5_to_4(gl_FragCoord.z); }
    #        else if (depth>d3) { move_6_to_5(gl_FragCoord.z); }
    #        else if (depth>d2) { move_7_to_6(gl_FragCoord.z); }
    #        else if (depth>d1) { move_8_to_7(gl_FragCoord.z); }
    #        else               { move_9_to_8(gl_FragCoord.z); }"""
    #        max2Dtextures = 0
    #        maxrendertargets = 8
    elif type == MYGL_PARTICLE_UPDATE:
        uservars = """
        uniform float size;
        uniform float step;
        uniform vec3 forces;
        uniform vec3 jitter;
        uniform vec3 initialspeed;
        uniform vec3 initialpos;
        uniform vec2 xedge;
        uniform vec2 yedge;
        uniform vec2 zedge;
        varying vec2 coord;"""
        verttrans = """
        coord = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size);
        vertex.xy = vec2(vertex.xy*(size-1.0))+vec2(0.5);"""
        renderequation = """
        vec4 pos_time = texture2D(tex2D_1,coord);

        vec4 velocity_sample = texture2D(tex2D_2,coord);
        vec3 direction = vec3(velocity_sample.xyz-vec3(0.5))*2.0;
        float speed = velocity_sample.w;
        vec3 velocity = speed*direction;

        pos_time.w += step;

        if(pos_time.w>=1.0){
            velocity = initialspeed + vec3(jitter*vec3(texture2D(tex2D_3,coord).xyz-vec3(0.5)));
            pos_time.xyz = initialpos + vec3(0.5);
            pos_time.w = 0.0;
        }

        velocity += forces;

        pos_time.xyz += velocity;
        if      (pos_time.x<0.0) { pos_time.x=0.0;velocity.x*=xedge.x; }
        else if (pos_time.x>1.0) { pos_time.x=1.0;velocity.x*=xedge.y; }
        if      (pos_time.y<0.0) { pos_time.y=0.0;velocity.y*=yedge.x; }
        else if (pos_time.y>1.0) { pos_time.y=1.0;velocity.y*=yedge.y; }
        if      (pos_time.z<0.0) { pos_time.z=0.0;velocity.z*=zedge.x; }
        else if (pos_time.z>1.0) { pos_time.z=1.0;velocity.z*=zedge.y; }
        color = pos_time;
        float new_speed = length(velocity);
        color2 = vec4(vec3(normalize(velocity.xyz)*0.5)+vec3(0.5),new_speed);"""
        max2Dtextures = 4
        maxrendertargets = 2
    elif type == MYGL_PARTICLE_DRAW:
        uservars = """uniform float size;
        uniform float fade;
        uniform vec3 trans;
        uniform vec3 scale;
        uniform float point_size;
        varying float continuing;
        varying float intensity;"""
        verttrans = """
        vec2 coord = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size);
        vec4 pos_time = texture2D(tex2D_1,coord);
        intensity = 1.0 - pow(pos_time.w,fade);
        vertex.xyz = pos_time.xyz - vec3(0.5);
        vertex = vec4(vec3(vertex.xyz*scale*2.0)+trans,1.0);"""
        postvertex = """
        continuing = 0.0;
        gl_PointSize = point_size/length(gl_Position);
        continuing = clamp(floor(gl_PointSize),0.0,1.0);"""
        renderequation = """
        vec2 v_rot = normalize(vertex.zw);
        vec4 l_uv = vec4(0.0,0.0,gl_PointCoord.xy);
        l_uv.zw-=vec2(0.5,0.5);l_uv.x=l_uv.z*v_rot.x;l_uv.y=l_uv.w*v_rot.x;l_uv.x-=l_uv.w*v_rot.y;l_uv.y+=l_uv.z*v_rot.y;
        color = texture2D(tex2D_2,l_uv.xy+vec2(0.5,0.5));
        color.a *= intensity*continuing;"""
        max2Dtextures = 2
    elif type == MYGL_CLOTH_DISTANCE:
        # doesn't need to take into account scale, trans, and is in the range [0,1]
        # instead of [-1,1] in 3D, b/c the corresponding update and normal shaders don't either.
        uservars = """
        uniform vec2 size;
        uniform float stretched;
        uniform bool repeat_x;
        uniform bool repeat_y;
        varying vec4 kernel_edges;
        varying vec4 kernel_corners;"""
        verttrans = """
        vec2 vert_uv; vec2 delta; vec2 delta_coord; float distance; vec3 vert;
        vert_uv = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size);
        vec3 center = texture2D(tex2D_1,vert_uv).rgb;

        for (int x_uv=-1; x_uv<1+1; x_uv++) { for (int y_uv=-1; y_uv<1+1; y_uv++) {
            if (!((x_uv==0)&&(y_uv==0))) {
                delta = vec2(float(x_uv),float(y_uv));
                delta_coord = delta/vec2(size);
                vert_uv = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size)+delta_coord;

                vert = texture2D(tex2D_1,vert_uv).rgb;
                distance = length(center-vert);
                distance *= stretched;
                if      (y_uv==-1) { if      (x_uv==-1) { kernel_corners.x = distance; }
                                     else if (x_uv== 0) { kernel_edges.x   = distance; }
                                     else if (x_uv== 1) { kernel_corners.y = distance; } }
                else if (y_uv== 0) { if      (x_uv==-1) { kernel_edges.y   = distance; }
                                     else if (x_uv== 1) { kernel_edges.z   = distance; } }
                else if (y_uv== 1) { if      (x_uv==-1) { kernel_corners.z = distance; }
                                     else if (x_uv== 0) { kernel_edges.w   = distance; }
                                     else if (x_uv== 1) { kernel_corners.w = distance; } }
            }
        }}
        vertex.xy = vec2(vertex.xy*(size-1.0))+vec2(0.5);"""
        renderequation = """
        color  = kernel_edges;
        color2 = kernel_corners;"""
        max2Dtextures = 2
        maxrendertargets = 2
    elif type == MYGL_CLOTH_UPDATE:
        uservars = """
        uniform vec2 size;
        uniform vec3 gravity;
        uniform float dampening;
        uniform float max_jitter;
        uniform float tensor;
        uniform float angle_tensor;
        uniform float time_step;
        uniform int kernel_size;
        uniform bool repeat_x;
        uniform bool repeat_y;

        varying vec3 position;
        varying vec3 velocity;
        varying float restrained;
        varying vec3 vec;"""
        verttrans = """
        vec2 coord = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size);
        vec4 position_restrained = texture2D(tex2D_1,coord);
        velocity                 = 2.0*vec3(texture2D(tex2D_2,coord).rgb-vec3(0.5));
        vec3 inter_cloth_forces  = vec3(0.0);
        vec4 kernel_edges        = texture2D(tex2D_3,coord);
        vec4 kernel_corners      = texture2D(tex2D_4,coord);
        position = position_restrained.rgb; //in range [0,1]; not [-1,1]
        restrained = position_restrained.a;

        if (restrained==0.0) {
            vec2 delta; vec2 delta_coord;
            vec3 spring_vec; vec3 norm_spring_vec; vec3 force_vec;
            float vec_length; float target_length=0.0; float force_scalar; bool continuing;
            vec3 left_edge = vec3(0.0);
            vec3 right_edge = vec3(0.0);
            vec3 top_edge = vec3(0.0);
            vec3 bottom_edge = vec3(0.0);
            for (int x_uv=-kernel_size; x_uv<kernel_size+1; x_uv++) { for (int y_uv=-kernel_size; y_uv<kernel_size+1; y_uv++) {
                if (!((x_uv==0)&&(y_uv==0))) {
                    delta = vec2(float(x_uv),float(y_uv));
                    delta_coord = delta/vec2(size);
                    coord = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size)+delta_coord;
                    continuing = true;
                    if (!((coord.x>0.0)&&(coord.x<1.0))) {
                        if (repeat_x) { coord.x = mod(coord.x,1.0); }
                        else { continuing = false; }
                    }
                    if (!((coord.y>0.0)&&(coord.y<1.0))) {
                        if (repeat_y) { coord.y = mod(coord.y,1.0); }
                        else { continuing = false; }
                    }
                    if (continuing) {
                        spring_vec = position - texture2D(tex2D_1,coord).rgb;
                        vec_length = length(spring_vec);
                        norm_spring_vec = spring_vec / vec_length;
                        if      (y_uv==-1) { if      (x_uv==-1) { target_length = kernel_corners.x;                                }
                                             else if (x_uv== 0) { target_length = kernel_edges.x  ; bottom_edge = norm_spring_vec; }
                                             else if (x_uv== 1) { target_length = kernel_corners.y;                                } }
                        else if (y_uv== 0) { if      (x_uv==-1) { target_length = kernel_edges.y  ;   left_edge = norm_spring_vec; }
                                             else if (x_uv== 1) { target_length = kernel_edges.z  ;  right_edge = norm_spring_vec; } }
                        else if (y_uv== 1) { if      (x_uv==-1) { target_length = kernel_corners.z;                                }
                                             else if (x_uv== 0) { target_length = kernel_edges.w  ;    top_edge = norm_spring_vec; }
                                             else if (x_uv== 1) { target_length = kernel_corners.w;                                } }
                        //distance correction
                        if (target_length!=0.0) {
                            force_scalar = (vec_length-target_length)/target_length;
                            force_vec = force_scalar*-norm_spring_vec;
                            inter_cloth_forces += force_vec;
                        }
                    }
                }
            }}
            inter_cloth_forces *= tensor;

            //angle correction
            float trig; vec3 bisect_vec;
            float cos_angle = dot(left_edge,right_edge);
            if ((cos_angle>0.0)&&(cos_angle<1.0)) {
                trig = acos(cos_angle)/3.1416;
                vec = left_edge + right_edge;
                vec_length = length(vec);
                if ((vec_length<=2.0)&&(vec_length!=0.0)) {
                    bisect_vec = vec/vec_length;
                    inter_cloth_forces += bisect_vec*(1.0-pow(trig,16.0))*angle_tensor;
                }
            }

            float jitter_length = length(inter_cloth_forces);
            if (jitter_length>max_jitter) { inter_cloth_forces *= 0.5; }

            velocity += vec3(inter_cloth_forces);
            velocity *= dampening;
            velocity += gravity;
            position += time_step*velocity;
        }
        vertex.xy = vec2(vertex.xy*(size-1.0))+vec2(0.5);"""
        renderequation = """
        color.rgb  = position;
        color.a = restrained;
        color2.rgb = vec3(velocity*0.5)+vec3(0.5);
        color3.rgb = vec3(vec*0.5)+vec3(0.5);"""
        max2Dtextures = 4
        maxrendertargets = 3
    elif type == MYGL_CLOTH_COLLIDE:
        uservars = """
        uniform int num_obstacles;
        uniform vec2 size;
        uniform float texture_size;
        varying vec3 position;
        varying vec3 velocity;
        varying float restrained;
        varying vec3 pass_through;
        const float two_pi = 6.283185;"""
        extfuncvert = """
        bool voxel_sample_sum_is_zero(vec2 coord) {
            return sum(texture2D(tex2D_4,coord))==0.0;
        }"""
        verttrans = """
        vec2 coord = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size);
        vec4 position_restrained = texture2D(tex2D_1,coord);
        velocity                 = 2.0*vec3(texture2D(tex2D_2,coord).rgb-vec3(0.5));
        vec3 vec                 = 2.0*vec3(texture2D(tex2D_3,coord).rgb-vec3(0.5));
        position = position_restrained.rgb; //in range [0,1]; not [-1,1]
        restrained = position_restrained.a;
        pass_through = vec3(0.0,0.0,0.0);
        //pass_through = vec3(   texture2D(  tex2D_6,  vec2(vertex.xy*vec2(1.0-(1.0/512.0)))+vec2(0.5/512.0)  ).a   );
        //pass_through = vec3(pass_through*2.0)-vec3(1.0);
        if (restrained==0.0) {
            vec4 obstacle_param1;vec4 obstacle_param2;vec3 obstacle_pos;
            float radius; float f_num_obstacles = float(num_obstacles);

            float xangle; float yangle; float cos_xangle; float sin_xangle; float cos_yangle; float sin_yangle; float maximum;
            vec3 transformed_position; vec3 abs_delta_vec; vec3 add_vec; mat3 rot_x; mat3 rot_y;
            for (int obstacle=0;obstacle<num_obstacles;obstacle++) {
                coord = vec2( ((float(obstacle)/(f_num_obstacles-1.0))*(1.0-(1.0/f_num_obstacles)))+(0.5/f_num_obstacles), 0.5 );
                obstacle_param1 = texture2D(tex2D_4,coord);
                obstacle_pos = 2.0*obstacle_param1.xyz;
                if (obstacle_pos.x>=1.0) { //polygonal object
                    vec4 obstacle = texture2D(tex2D_6,vec2(-position.z,position.y));
                    //float zdist = abs(  ((obstacle.a*2.0)-1.0)  -  (2.0*(position.z-0.5))  );
                    //pass_through = vec3(zdist);
                    //if (zdist<(0.5/texture_size)) {
                        //restrained = 1.0;
                        //pass_through = vec3(obstacle.xyz);
                        //if (abs(sum(obstacle.xyz)-3.0)<0.1) { restrained=1.0; }
                    //}
                    vec4 obstacle2 = texture2D(tex2D_7,position.xy);
                    if (abs(sum(obstacle.xyz)-3.0)<0.1) { restrained=1.0; }
                    if (  (sum(obstacle.xyz)!=0.0)  &&  (sum(obstacle2.xyz)!=0.0)  ) {
                        //restrained=1.0;
                        position += (obstacle.xyz-vec3(0.5))*0.001;
                        velocity = vec3(0.0);
                    }
                }

                else if (obstacle_pos.z>=1.0) { //sphere
                    obstacle_pos.z -= 1.0;
                    vec = position - obstacle_pos;
                    radius = obstacle_param1.a;
                    if (length(vec)<radius) {
                        vec3 vec_normalized = normalize(vec);
                        position = vec_normalized*radius+obstacle_pos;
                        velocity *= 0.9;
                }}
                else if (obstacle_pos.y>=1.0) { //box
                    obstacle_pos.y -= 1.0;
                    obstacle_param2 = texture2D(tex2D_5,coord);
                    xangle = obstacle_param2.z*two_pi;
                    yangle = obstacle_param2.w*two_pi;
                    cos_xangle=cos(-xangle);sin_xangle=sin(-xangle);cos_yangle=cos(-yangle);sin_yangle=sin(-yangle);
                    rot_x = mat3(vec3(        1.0, 0.0,        0.0       ),
                                 vec3(        0.0, cos_yangle,-sin_yangle),
                                 vec3(        0.0, sin_yangle, cos_yangle));
                    rot_y = mat3(vec3( cos_xangle,        0.0, sin_xangle),
                                 vec3(        0.0,        1.0, 0.0       ),
                                 vec3(-sin_xangle,        0.0, cos_xangle));
                    transformed_position = rot_x*rot_y*(position-obstacle_pos.xyz);
                    if (((transformed_position.x>-obstacle_param1.w)&&(transformed_position.x<obstacle_param1.w))&&
                        ((transformed_position.y>-obstacle_param2.x)&&(transformed_position.y<obstacle_param2.x))&&
                        ((transformed_position.z>-obstacle_param2.y)&&(transformed_position.z<obstacle_param2.y))) {
                        abs_delta_vec = abs(transformed_position);
                        maximum = max(abs_delta_vec.x,max(abs_delta_vec.y,abs_delta_vec.z));
                        add_vec = vec3(0.0);
                        if      (maximum==abs_delta_vec.x) { add_vec = vec3((obstacle_param1.w-maximum)*sign(transformed_position.x),0.0,0.0); }
                        else if (maximum==abs_delta_vec.y) { add_vec = vec3(0.0,(obstacle_param2.x-maximum)*sign(transformed_position.y),0.0); }
                        else                               { add_vec = vec3(0.0,0.0,(obstacle_param2.y-maximum)*sign(transformed_position.z)); }

                        cos_xangle=cos(xangle);sin_xangle=sin(xangle);cos_yangle=cos(yangle);sin_yangle=sin(yangle);
                        mat3 rot_x = mat3(vec3(        1.0, 0.0,        0.0       ),
                                          vec3(        0.0, cos_yangle,-sin_yangle),
                                          vec3(        0.0, sin_yangle, cos_yangle));
                        mat3 rot_y = mat3(vec3( cos_xangle,        0.0, sin_xangle),
                                          vec3(        0.0,        1.0, 0.0       ),
                                          vec3(-sin_xangle,        0.0, cos_xangle));
                        add_vec = rot_x*rot_y*add_vec;

                        position += add_vec;
                        velocity *= 0.9;
                }}
            }
        }
        vertex.xy = vec2(vertex.xy*(size-1.0))+vec2(0.5);"""
        renderequation = """
        color.rgb  = position;
        color.a = restrained;
        color2.rgb = vec3(velocity*0.5)+vec3(0.5);
        color3.rgb = pass_through;//for debugging"""
        max2Dtextures = 7
        maxrendertargets = 3
    elif type == MYGL_CLOTH_NORMAL:
        uservars = """
        uniform vec2 size;
        uniform bool repeat_x;
        uniform bool repeat_y;
        uniform bool normal_flip;"""
        verttrans = """
        vec2 vert_uv = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size);
        vec3 center = texture2D(tex2D_1,vert_uv).rgb;

        vec2 temp_vert_uv=vec2(0.0);
        vec3 temp_vert;
        vec3 diff_vec1=vec3(0.0);vec3 diff_vec2=vec3(0.0);vec3 diff_vec3=vec3(0.0);vec3 diff_vec4=vec3(0.0);

        float invert = -1.0;

        temp_vert_uv = vert_uv + (vec2(1.0,0.0)/size);
        if ((temp_vert_uv.x>1.0)&&(!repeat_x)) { diff_vec1 = vec3(0.0); }
        else {
            temp_vert = texture2D(tex2D_1,temp_vert_uv).rgb;
            diff_vec1 = normalize(temp_vert-center);
        }

        temp_vert_uv = vert_uv + (vec2(-1.0,0.0)/size);
        if ((temp_vert_uv.x<0.0)&&(!repeat_x)) { diff_vec2 = vec3(0.0); }
        else {
            temp_vert = texture2D(tex2D_1,temp_vert_uv).rgb;
            diff_vec2 = normalize(temp_vert-center);
        }

        temp_vert_uv = vert_uv + (vec2(0.0,1.0)/size);
        if ((temp_vert_uv.y>1.0)&&(!repeat_y)) { diff_vec3 = vec3(0.0); }
        else {
            temp_vert = texture2D(tex2D_1,temp_vert_uv).rgb;
            diff_vec3 = normalize(temp_vert-center);
        }

        temp_vert_uv = vert_uv + (vec2(0.0,-1.0)/size);
        if ((temp_vert_uv.y<0.0)&&(!repeat_y)) { diff_vec4 = vec3(0.0); }
        else {
            temp_vert = texture2D(tex2D_1,temp_vert_uv).rgb;
            diff_vec4 = normalize(temp_vert-center);
        }

        vec3 x_vec = diff_vec1 - diff_vec2;
        vec3 y_vec = diff_vec3 - diff_vec4;

        realnorm = invert*normalize(cross(y_vec,x_vec));
        if (normal_flip) { realnorm *= -1.0; }

        vertex.xy = vec2(vertex.xy*(size-1.0))+vec2(0.5);"""
        renderequation = """
        color.rgb = (0.5*realnorm)+vec3(0.5);"""
    elif type == MYGL_CLOTH_DRAW:
        uservars = """
        uniform vec2 size;
        uniform vec2 uv_repeat;
        uniform bool has_texture;
        uniform int numlights;
        varying vec2 cloth_uv;"""
        prevertex = """
        cloth_uv = vertex.xy;
        vec2 coord = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size);
        realnorm = 2.0*(texture2D(tex2D_3,coord).rgb-vec3(0.5));"""
        verttrans = """
        vertex.xyz = texture2D(tex2D_1,coord).rgb-vec3(0.5);
        vertex = vec4(vec3(vertex.xyz*2.0),1.0);"""  # scale is 2.0*the scale.
        renderequation = """
        normal = normalize(normal);

        vec3 ambient_light = vec3(0.0,0.0,0.0);
        vec3 diffuse_light = vec3(0.0,0.0,0.0);
        vec3 specular_light = vec3(0.0,0.0,0.0);
        float attenuation = 0.0;

        attenuation = light_att(light1);
        ambient_light  += light_ambient(light1).rgb;
        diffuse_light  += light_diffuse(light1).rgb*attenuation;
        specular_light += light_specular_ph(light1).rgb*attenuation;
        if (numlights>1) {
            attenuation = light_att(light2);
            ambient_light  += light_ambient(light2).rgb;
            diffuse_light  += light_diffuse(light2).rgb*attenuation;
            specular_light += light_specular_ph(light2).rgb*attenuation;
            if (numlights>2) {
                attenuation = light_att(light3);
                ambient_light  += light_ambient(light3).rgb;
                diffuse_light  += light_diffuse(light3).rgb*attenuation;
                specular_light += light_specular_ph(light3).rgb*attenuation;
                if (numlights>3) {
                    attenuation = light_att(light4);
                    ambient_light  += light_ambient(light4).rgb;
                    diffuse_light  += light_diffuse(light4).rgb*attenuation;
                    specular_light += light_specular_ph(light4).rgb*attenuation;
                    if (numlights>4) {
                        attenuation = light_att(light5);
                        ambient_light  += light_ambient(light5).rgb;
                        diffuse_light  += light_diffuse(light5).rgb*attenuation;
                        specular_light += light_specular_ph(light5).rgb*attenuation;
                        if (numlights>5) {
                            attenuation = light_att(light6);
                            ambient_light  += light_ambient(light6).rgb;
                            diffuse_light  += light_diffuse(light6).rgb*attenuation;
                            specular_light += light_specular_ph(light6).rgb*attenuation;
                            if (numlights>6) {
                                attenuation = light_att(light7);
                                ambient_light  += light_ambient(light7).rgb;
                                diffuse_light  += light_diffuse(light7).rgb*attenuation;
                                specular_light += light_specular_ph(light7).rgb*attenuation;
                                if (numlights>7) {
                                    attenuation = light_att(light8);
                                    ambient_light  += light_ambient(light8).rgb;
                                    diffuse_light  += light_diffuse(light8).rgb*attenuation;
                                    specular_light += light_specular_ph(light8).rgb*attenuation;
        }}}}}}}

        color.rgb += ambient_color.rgb*ambient_light;
        color.rgb += diffuse_color.rgb*diffuse_light;
        color.rgb += specular_color.rgb*specular_light;
        if (has_texture) { color *= texture2D(tex2D_2,cloth_uv*uv_repeat); }"""
        max2Dtextures = 3
    elif type == MYGL_HAIR_GROW:
        uservars = """
        uniform vec2 size;
        uniform float length_scalar;
        varying vec2 coord;"""
        verttrans = """
        coord = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size);
        vertex.xy = vec2(vertex.xy*(size-1.0))+vec2(0.5);"""
        renderequation = """
        color.rgb = texture2D(tex2D_1,coord).xyz;
        color.rgb += length_scalar*texture2D(tex2D_2,coord).xyz;"""
        max2Dtextures = 2
    elif type == MYGL_HAIR_UPDATE:
        uservars = """
        uniform vec2 size;
        uniform vec3 gravity;
        uniform float dampening;
        uniform float tensor;
        uniform bool end;
        uniform float target_length;
        uniform float time_step;

        varying vec3 position;
        varying vec3 velocity;
        varying float current_length;"""
        verttrans = """
        vec2 coord = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size);
        vec3 last_position   = texture2D(tex2D_1,coord).xyz;
        vec4 position_length = texture2D(tex2D_2,coord);
        vec3 next_position   = texture2D(tex2D_3,coord).xyz;
        velocity             = 2.0*vec3(texture2D(tex2D_4,coord).rgb-vec3(0.5));
        position = position_length.rgb; //in range [0,1]; not [-1,1]
        current_length = position_length.a;

        vec3 inter_hair_forces = vec3(0.0);

        vec3 spring_vec = position - last_position;
        float vec_length = length(spring_vec);
        //distance correction
        float force_scalar = (vec_length-target_length)/target_length;
        inter_hair_forces += force_scalar*(spring_vec/-vec_length);

        /*
        if (!end) {
            spring_vec = position - next_position;
            vec_length = length(spring_vec);
            //distance correction
            force_scalar = (vec_length-target_length)/target_length;
            inter_hair_forces += force_scalar*(spring_vec/-vec_length);
        }
        */

        inter_hair_forces *= tensor;
        velocity += vec3(inter_hair_forces);
        velocity *= dampening;
        velocity += gravity;
        position += time_step*velocity;

        vertex.xy = vec2(vertex.xy*(size-1.0))+vec2(0.5);"""
        renderequation = """
        color.rgb  = position;
        color.a = current_length;
        color2.rgb = vec3(velocity*0.5)+vec3(0.5);"""
        max2Dtextures = 4
        maxrendertargets = 2
    elif type == MYGL_HAIR_DRAW:
        uservars = """
        uniform vec2 size;
        uniform float scale;
        uniform vec3 trans;
        uniform int hair_length;
        uniform vec3 camerapos;
        uniform float hair_size;
        attribute float quad_side;"""
        prevertex = """
        vec2 coord2D = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size);
        //realnorm = 2.0*(texture2D(tex2D_3,coord2D).rgb-vec3(0.5));"""
        verttrans = """
        vec4 sample=vec4(0.0);
        float texture = floor(float(hair_length-1)*vertex.z)+1.0;
        if      (abs(texture-1.0)<0.01) { sample = texture2D(tex2D_1,coord2D); }
        else if (abs(texture-2.0)<0.01) { sample = texture2D(tex2D_2,coord2D); }
        else if (abs(texture-3.0)<0.01) { sample = texture2D(tex2D_3,coord2D); }
        else if (abs(texture-4.0)<0.01) { sample = texture2D(tex2D_4,coord2D); }
        else if (abs(texture-5.0)<0.01) { sample = texture2D(tex2D_5,coord2D); }
        else if (abs(texture-6.0)<0.01) { sample = texture2D(tex2D_6,coord2D); }
        else if (abs(texture-7.0)<0.01) { sample = texture2D(tex2D_7,coord2D); }
        else if (abs(texture-8.0)<0.01) { sample = texture2D(tex2D_8,coord2D); }
        vertex.xyz = sample.rgb-vec3(0.5);
        vec2 camera_vec = normalize(vec3(camerapos-vertex.xyz).xz);
        if (quad_side==1.0) {
            vertex.x -= hair_size*camera_vec.y;
            vertex.z += hair_size*camera_vec.x;
        }
        else {
            vertex.x += hair_size*camera_vec.y;
            vertex.z -= hair_size*camera_vec.x;
        }
        vertex = vec4(vec3(vertex.xyz*scale)+trans,1.0);"""
        renderequation = """
        //normal = normalize(normal);
        //color.rgb += ambient_color.rgb*light_ambient(light1).rgb;
        //color.rgb += diffuse_color.rgb*light_diffuse(light1).rgb;
        //color.rgb += specular_color.rgb*light_specular_ph(light1).rgb;
        color.rgb = vec3(1.0);"""
        max2Dtextures = 16
    #    elif type == MYGL_FLUID2D_DIFFUSE:
    #        uservars = "uniform vec2 size;"
    #        extfuncfrag = """
    #        def lin_solve(N, b, x, x0, a, c):
    #            for k in range(0,20):
    #                numerator = x0[1:N+1,1:N+1] +
    #                            a*(x[0:N,1:N+1]+x[2:N+2,1:N+1]+x[1:N+1,0:N]+x[1:N+1,2:N+2])
    #                x[1:N+1,1:N+1] = numerator/c
    #            set_bnd(N, b, x)
    #        vec3 add_source(float dt) {
    #            return dt*2.0*(texture2D(tex2,gl_FragCoord.xy/size).rg-vec2(0.5));
    #        }
    #        diffuse(vec2 size, float b, float diff, float dt) {
    #            float a = dt*diff*size.x*size.y;
    #            lin_solve(N,b,x,x0,a,1.0+4.0*a)
    #        }"""
    #        renderequation = """
    #        diffuse(N, 0, x, x0, diff, dt);
    #        //color = vec4(1.0);"""
    #        max2Dtextures = 1
    #    elif type == MYGL_FLUID2D_ADVECT:
    #        extfuncfrag = """
    #        def advect (N, b, d, d0, u, v, dt):
    #            dt0 = dt*N;
    #            for i in range(1, N+1):
    #                for j in range(1, N+1):
    #                    x = i-dt0*u[i,j]; y = j-dt0*v[i,j]
    #                    vec2 xy = gl_FragCoord.xy-dt0*texture2D(tex2,);
    #                    if x<0.5: x=0.5
    #                    if x>N+0.5: x=N+0.5
    #                    i0 = floor(x); i1 = i0+1
    #                    if y<0.5: y=0.5
    #                    if y>N+0.5: y=N+0.5
    #                    j0 = floor(y); j1 = j0+1
    #                    s1 = x-i0; s0 = 1-s1; t1 = y-j0; t0 = 1-t1
    #                    d[i,j] = s0*(t0*texture2D(tex1,[i0,j0])+t1*d0[i0,j1])+
    #                             s1*(t0*d0[i1,j0]+t1*d0[i1,j1]);
    #            set_bnd (N, b, d)"""
    #        renderequation = """
    #        advect(N, 0, x, x0, v, dt);"""
    #        max2Dtextures = 2
    #    elif type == MYGL_FLUID3D_UPDATE:
    #        uservars = "uniform float add_force; uniform vec2 add_force_at;"
    #        renderequation = """
    #        float h11 = texture2D(tex2D_1,uv).r;
    #
    #        float l = length(add_force_at-uv);
    #        h11 += clamp(add_force*1.0/l,0.0,1.0);
    #
    #        float delta = 1.0/512.0;
    #        float h00 = texture2D(tex2D_1,uv+vec2(-delta,-delta)).r;
    #        float h10 = texture2D(tex2D_1,uv+vec2(   0.0,-delta)).r;
    #        float h20 = texture2D(tex2D_1,uv+vec2( delta,-delta)).r;
    #        float h01 = texture2D(tex2D_1,uv+vec2(-delta,   0.0)).r;
    #        float h21 = texture2D(tex2D_1,uv+vec2( delta,   0.0)).r;
    #        float h02 = texture2D(tex2D_1,uv+vec2(-delta, delta)).r;
    #        float h12 = texture2D(tex2D_1,uv+vec2(   0.0, delta)).r;
    #        float h22 = texture2D(tex2D_1,uv+vec2( delta, delta)).r;
    #
    #        h11 = ( h00+h10+h20 + h01+h11+h20 + h02+h12+h22 ) / 9.0;
    #
    #        color.r = h11;"""
    #    elif type == MYGL_FLUID3D_DRAW:
    #        uservars = "uniform float near; uniform float far;"
    #        renderequation = """
    #        vec4 s1 = texture2D(tex2D_1,uv); if (sum(s1.xyz)<0.001) { discard; }
    #        vec4 s2 = texture2D(tex2D_2,uv); if (sum(s2.xyz)<0.001) { discard; }
    #
    #        vec3 diff = s2.xyz - s1.xyz;
    #        if (s1.xyz==s2.xyz) { discard; }
    #        vec3 raydir = normalize(diff);
    #
    #        float step = 8.0/512.0;
    #        raydir *= step;
    #
    #        vec3 raypos = s1.xyz;
    #        vec3 sample = vec3(0.0,0.0,0.0);
    #
    #        float i = 0.0;
    #        float maximum = 512.0;
    #        while (true) {
    #            sample = texture2D(tex2D_3,raypos.xz).rgb;
    #            if (raypos.y<sample.r) {
    #                color.rgb = sample;
    #                break;
    #            }
    #            raypos += raydir;
    #            if ((raypos.x<0.0)||(raypos.x>1.0)) { discard; break; }
    #            if ((raypos.y<0.0)||(raypos.y>1.0)) { discard; break; }
    #            if ((raypos.z<0.0)||(raypos.z>1.0)) { discard; break; }
    #
    #            i += 1.0;
    #            if (i>maximum) { color.rgb = vec3(1.0,1.0,1.0); discard; break; }
    #        }
    #        float traveled = length(raypos-s1.xyz);
    #        float a = far/(far-near);
    #        float b = (far*near)/(near-far);
    #        traveled = (a+b)/traveled;
    #        //gl_FragDepth = s1.a+traveled;
    #        //color.rgb = vec3(gl_FragDepth);"""
    elif type == MYGL_SOFTPHYS_UPDATE:
        uservars = """
        uniform vec2 size;
        uniform vec3 level_size;
        varying vec3 position;
        varying vec3 velocity;
        varying float restrained;

        uniform vec3 trans;
        uniform vec3 new_forces;
        uniform vec3 target_lengths;

        const float convergence = -0.01;"""
        extfuncvert = """
        vec4 get_pos_data(vec2 at_coord) {
            vec4 pos_r_vec = texture2D(tex2D_1,at_coord);
            vec3 pos = 2.0*(pos_r_vec.rgb-vec3(0.5)); //in range [-1,1]
            return vec4(pos,pos_r_vec.a);
        }
        void add_to_velocity(vec2 at_coord,float target_length) {
            vec4 new_pos_r = get_pos_data(at_coord);
            vec3 delta_vec = new_pos_r.xyz - position;
            velocity += (target_length-length(delta_vec))*convergence*delta_vec;
        }"""
        verttrans = """
        vec2 coord = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size);

        vec4 position_restrained = get_pos_data(coord);
        position = position_restrained.xyz;
        restrained = position_restrained.a;

        velocity = 2.0*vec3(texture2D(tex2D_2,coord).rgb-vec3(0.5));
        velocity += new_forces;

        vec2 boo_coord = coord - vec2(1.0/(size.x-1.0),              0.0);
        vec2 uoo_coord = coord + vec2(1.0/(size.x-1.0),              0.0);
        vec2 oob_coord = coord - vec2(             0.0, 1.0/(size.y-1.0));
        vec2 oou_coord = coord + vec2(             0.0, 1.0/(size.y-1.0));
        vec2 obo_coord = coord - vec2(    level_size.x,              0.0);
        vec2 ouo_coord = coord + vec2(    level_size.x,              0.0);

        vec2 bbb_coord = coord - vec2( (1.0/(size.x-1.0))+level_size.x, 1.0/(size.y-1.0));
        vec2 bbu_coord = coord - vec2( (1.0/(size.x-1.0))+level_size.x,-1.0/(size.y-1.0));
        vec2 ubb_coord = coord - vec2(-(1.0/(size.x-1.0))+level_size.x,-1.0/(size.y-1.0));
        vec2 ubu_coord = coord - vec2(-(1.0/(size.x-1.0))+level_size.x,-1.0/(size.y-1.0));
        vec2 bub_coord = coord - vec2( (1.0/(size.x-1.0))-level_size.x, 1.0/(size.y-1.0));
        vec2 buu_coord = coord - vec2( (1.0/(size.x-1.0))-level_size.x,-1.0/(size.y-1.0));
        vec2 uub_coord = coord - vec2(-(1.0/(size.x-1.0))-level_size.x,-1.0/(size.y-1.0));
        vec2 uuu_coord = coord - vec2(-(1.0/(size.x-1.0))-level_size.x,-1.0/(size.y-1.0));
        """
        for side, swiz, sign in [["boo", "x", "<"], ["uoo", "x", ">"], ["oob", "y", "<"], ["oou", "y", ">"]]:
            verttrans += """
            if (mod(""" + side + "_coord." + swiz + ",level_size." + swiz + ")" + sign + "mod(coord." + swiz + ",level_size." + swiz + """)) {
                add_to_velocity(""" + side + "_coord,target_lengths." + swiz + """);
            }
            """
        for side, com1, act1, act2, com2 in [["ouo", ">1.0", "-=", "+=", "<1.0"], ["obo", "<0.0", "+=", "-=", ">0.0"]]:
            verttrans += """
            if (""" + side + "_coord.x" + com1 + """) {
                """ + side + "_coord.x " + act1 + """ 1.0;
                """ + side + "_coord.y " + act2 + """ level_size.z;
            }
            if (""" + side + "_coord.y" + com2 + """) {
                add_to_velocity(""" + side + """_coord,target_lengths.y);
            }"""
        #        verttrans += "float diagonal_length = length(target_lengths);"
        #        for side in [["bbb"],["bbu"],["ubb"],["ubu"],["bub"],["buu"],["uub"],["uuu"]]:
        #            verttrans += """
        #            if ("""+side+"_coord.x"+com1+""") {
        #                """+side+"_coord.x "+act1+""" 1.0;
        #                """+side+"_coord.y "+act2+""" level_size.z;
        #            }
        #            """
        verttrans += """
        position += velocity;
        velocity *= 0.97;
        position += trans;

        vertex.xy = vec2(vertex.xy*(size-1.0))+vec2(0.5);"""
        renderequation = """
        color.rgb = (position+vec3(1.0))/2.0;
        color.a = restrained;
        color2.rgb = vec3(velocity*0.5)+vec3(0.5);"""
        maxrendertargets = 2
    elif type == MYGL_SOFTPHYS_COLLIDE:
        uservars = """
        uniform vec2 size;
        varying vec3 position;
        varying vec3 velocity;
        varying float restrained;

        uniform float bounce;"""
        verttrans = """
        vec2 coord = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size);

        vec4 position_restrained = texture2D(tex2D_1,coord);
        position = 2.0*(position_restrained.rgb-vec3(0.5)); //in range [-1,1]
        restrained = position_restrained.a;

        velocity = 2.0*vec3(texture2D(tex2D_2,coord).rgb-vec3(0.5));

        if (position.y<0.0) { position.y = 0.0; velocity.y *= bounce; }

        vertex.xy = vec2(vertex.xy*(size-1.0))+vec2(0.5);
        """
        renderequation = """
        color.rgb  = (position+vec3(1.0))/2.0;
        color.a = restrained;
        color2.rgb = vec3(velocity*0.5)+vec3(0.5);"""
        maxrendertargets = 2
    elif type == MYGL_SOFTPHYS_ADDFORCE:
        uservars = """
        uniform vec2 size;
        uniform vec3 force;
        varying vec3 velocity;"""
        verttrans = """
        vec2 coord = vec2(vertex.xy*vec2(1.0-(1.0/size)))+vec2(0.5/size);
        velocity = 2.0*vec3(texture2D(tex2D_1,coord).rgb-vec3(0.5));
        velocity += force;"""
        renderequation = "color.rgb = vec3(velocity*0.5)+vec3(0.5);"
    elif type == MYGL_INTERNAL_VOLUME_RAY:
        uservars = "uniform int stage;"
        renderequation = """
        if (stage==1) { color  = vec4(clamp(uvw,1.0/255.0,1.0),gl_FragCoord.z); color2.a = 0.0; }
        else          { color2 = vec4(clamp(uvw,1.0/255.0,1.0),gl_FragCoord.z); color.a  = 0.0; }"""
        maxrendertargets = 2
    elif type == MYGL_INTERNAL_VOLUME_DRAW:
        uservars = "uniform float near; uniform float far; uniform float step; uniform int max_steps;"
        renderequation = """
        vec4 s1 = texture2D(tex2D_2,uv); if (sum(s1.xyz)<0.001) { discard; }
        vec4 s2 = texture2D(tex2D_3,uv); if (sum(s2.xyz)<0.001) { discard; }

        vec3 diff = s2.xyz - s1.xyz;
        if (s1.xyz==s2.xyz) { discard; }
        vec3 raydir = normalize(diff);

        raydir *= step;

        vec3 raypos = s1.xyz;

        /*for (int i=0;i<max_steps;i++) {
            color.rgb += texture3D(tex3D_1,raypos.xyz).rgb;
            raypos += raydir;
            if ((raypos.x<0.0)||(raypos.x>1.0)) { break; }
            if ((raypos.y<0.0)||(raypos.y>1.0)) { break; }
            if ((raypos.z<0.0)||(raypos.z>1.0)) { break; }
            if (i>max_steps) { discard; break; }
        }*/
        color.rgba = vec4(1.0);
        //float traveled = length(raypos-s1.xyz);
        //float a = far/(far-near);
        //float b = (far*near)/(near-far);
        //traveled = (a+b)/traveled;
        //gl_FragDepth = s1.a+traveled;
        //color.rgb = vec3(gl_FragDepth);"""
        max3Dtextures = 1
    else:
        raise GLError('Shader type "' + str(type) + '" not found!')
    return renderequation, uservars, prevertex, verttrans, postvertex, uvtrans, extfuncvert, extfuncfrag, max1Dtextures, max2Dtextures, max3Dtextures, maxcubetextures, maxrendertargets


def mygl_internal_CompileShader(source, shaderType):
    shader = glCreateShaderObjectARB(shaderType)
    # Crucial for ATI compatibility to have []
    glShaderSourceARB(shader, [source])
    glCompileShaderARB(shader)
    return shader


def glLibUseShader(shader):
    if shader is not None:
        if hasattr(shader, "compiled"):
            if not shader.compiled:
                raise GLError("Shader not compiled!")
            else:
                shader = shader.program
        else:
            raise GLError("Shader not a GLShader object!")
    else:
        shader = 0
    glUseProgramObjectARB(shader)


class GLShader:
    def __init__(self):
        self.errors = "Shader not yet compiled!"
        self.compiled = False
        self.renderequation = ""
        self.uservars = ""
        self.prevertex = ""
        self.verttrans = ""
        self.postvertex = ""
        self.uvtrans = ""
        self.extfuncvert = ""
        self.extfuncfrag = ""
        self.max1Dtextures = 0
        self.max2Dtextures = 8
        self.max3Dtextures = 0
        self.maxcubetextures = 0
        self.maxdeptheffects = 1
        self.automaxtextures = False
        self.maxrendertargets = 1
        self.automaxrendertargets = False
        self.locations = {}
        self.str_name = str(self)

    def use_prebuilt(self, _type, args=None):
        if not self.compiled:
            self.renderequation, self.uservars, self.prevertex, self.verttrans, self.postvertex, self.uvtrans, self.extfuncvert, self.extfuncfrag, self.max1Dtextures, self.max2Dtextures, self.max3Dtextures, self.maxcubetextures, self.maxrendertargets = mygl_internal_shader(
                _type, args)
            self.compile()
            if self.errors != "": print(self.errors)
        else:
            raise GLError("Shader already compiled!")

    def render_equation(self, render_equation):
        self.renderequation = render_equation

    def user_variables(self, user_vars):
        self.uservars = user_vars

    def pre_vertex(self, pre_vertex):
        self.prevertex = pre_vertex

    def vertex_transform(self, vertex_transform):
        self.verttrans = vertex_transform

    def post_vertex(self, post_vertex):
        self.postvertex = post_vertex

    def uv_transform(self, uv_transform):
        self.uvtrans = uv_transform

    def vertex_extension_functions(self, extension_functions):
        self.extfuncvert = extension_functions

    def fragment_extension_functions(self, extension_functions):
        self.extfuncfrag = extension_functions

    def render_targets(self, render_targets):
        self.maxrendertargets = render_targets

    def auto_render_targets(self, value):
        self.automaxrendertargets = value

    def max_textures_1D(self, number):
        self.max1Dtextures = number

    def max_textures_2D(self, number):
        self.max2Dtextures = number

    def max_textures_3D(self, number):
        self.max3Dtextures = number

    def max_textures_cube(self, number):
        self.maxcubetextures = number

    def max_depth_effects(self, number):
        self.maxdeptheffects = number

    def auto_max_textures(self, value):
        self.automaxtextures = value

    def decompile(self):
        if not self.compiled: GLError("Shader already not compiled!")
        glDeleteProgram(self.program)
        del self.source, self.program
        self.errors = "Shader not yet compiled!"
        self.compiled = False
        self.locations = {}

    def compile(self, version="\n"):
        if not self.compiled:
            if self.automaxtextures:
                for target in [["tex1D_", 1], ["tex2D_", 2], ["tex3D_", 3], ["texCube_", 6]]:
                    texnum = target[1]
                    num = 0
                    while True:
                        num += 1
                        str_target = target[0] + str(num)
                        if str_target in self.renderequation: continue
                        if str_target in self.prevertex: continue
                        if str_target in self.verttrans: continue
                        if str_target in self.postvertex: continue
                        if str_target in self.uvtrans: continue
                        if str_target in self.extfuncvert: continue
                        if str_target in self.extfuncfrag: continue

                        if texnum == 1: self.max1Dtextures = num - 1
                        elif texnum == 2: self.max2Dtextures = num - 1
                        elif texnum == 3: self.max3Dtextures = num - 1
                        elif texnum == 6: self.maxcubetextures = num - 1

                        break
            if self.automaxrendertargets:
                num = 1
                while True:
                    num += 1
                    target = "color" + str(num)
                    if target in self.renderequation: continue
                    self.maxrendertargets = num - 1
                    break
            self.source = mygl_internal_ShaderSource(version, self.renderequation, self.uservars, self.prevertex, self.verttrans,
                                                     self.postvertex, self.uvtrans, self.extfuncvert, self.extfuncfrag,
                                                     self.max2Dtextures, self.max3Dtextures, self.maxcubetextures, self.maxdeptheffects,
                                                     self.maxrendertargets)

            self.program = glCreateProgramObjectARB()
            VertexShader = mygl_internal_CompileShader(self.source.vert, GL_VERTEX_SHADER_ARB)
            #        try:print glGetShaderInfoLog(VertexShader)
            #        except:print_log(VertexShader)
            FragmentShader = mygl_internal_CompileShader(self.source.frag, GL_FRAGMENT_SHADER_ARB)
            #        try:print glGetShaderInfoLog(FragmentShader)
            #        except:print_log(FragmentShader)
            glAttachObjectARB(self.program, VertexShader)
            glAttachObjectARB(self.program, FragmentShader)
            glDeleteObjectARB(VertexShader)
            glDeleteObjectARB(FragmentShader)
            glValidateProgramARB(self.program)
            glLinkProgramARB(self.program)
            #        print glGetProgramInfoLog(self.program)

            self.errors = glGetInfoLogARB(self.program)
            self.compiled = True
            return self.errors
        else:
            GLError("Shader already compiled!")

    def print_vertex(self):
        shader_lines = self.source.vert.split("\n")
        for string in self.mygl_internal_get_shader_strings(shader_lines,
                                                            self.prevertex + self.verttrans + self.postvertex + self.uservars,
                                                            True):
            print(string)

    def print_fragment(self):
        shader_lines = self.source.frag.split("\n")
        for string in self.mygl_internal_get_shader_strings(shader_lines, self.uvtrans + self.renderequation + self.uservars,
                                                            True):
            print(string)

    def save_vertex(self, numbers=False, path=None):
        shader_lines = self.source.vert.split("\n")
        file_string = ""
        for string in self.mygl_internal_get_shader_strings(shader_lines, "", numbers):
            file_string += string
            file_string += "\n"
        if path == None: file = open("vertex.vert", "w")
        else:            file = open(path, "w")
        file.write(file_string)
        file.close()

    def save_fragment(self, numbers=False, path=None):
        shader_lines = self.source.frag.split("\n")
        file_string = ""
        for string in self.mygl_internal_get_shader_strings(shader_lines, "", numbers):
            file_string += string
            file_string += "\n"
        if path == None: file = open("fragment.frag", "w")
        else:            file = open(path, "w")
        file.write(file_string)
        file.close()

    def mygl_internal_get_shader_strings(self, shader_lines, parts, numbering):
        first_indent = len(shader_lines[1]) - len(shader_lines[1].strip())
        last_rel_indent = 0
        line_num = 1
        max_num_length = len(str(len(shader_lines)))
        strings = []
        for shader_line_indent in shader_lines:
            shader_line = shader_line_indent.strip()
            indent = len(shader_line_indent) - len(shader_line)
            rel_indent = indent - first_indent
            if rel_indent != last_rel_indent:
                if shader_line in parts:
                    rel_indent = last_rel_indent
            line_num_str = str(line_num)
            string = ""
            if numbering:
                string += line_num_str + ": " + ((max_num_length - len(line_num_str)) * " ")
            string += rel_indent * " "
            string += shader_line
            strings.append(string)
            line_num += 1
            last_rel_indent = rel_indent
        return strings

    def mygl_internal_get_location(self, name):
        try: return self.locations[name]
        except:
            self.locations[name] = glGetUniformLocation(self.program, name.encode())
            if self.locations[name] == -1:
                if not mute_warnings: print(
                    'Could not pass to the variable "' + name + '"!  Check that the shader is enabled, the variable exists, and that it is used.')
        return self.locations[name]

    def pass_texture(self, tex_id, tex_type=MYGL_TEXTURE_2D, number=1):
        glActiveTexture(GL_TEXTURE0 + number - 1)
        active_texture = glGetIntegerv(GL_ACTIVE_TEXTURE) - GL_TEXTURE0
        if tex_id is None:
            glBindTexture(GL_TEXTURE_1D, 0)
            glBindTexture(GL_TEXTURE_2D, 0)
            glBindTexture(GL_TEXTURE_3D, 0)
            glBindTexture(GL_TEXTURE_CUBE_MAP, 0)
        elif tex_type == MYGL_TEXTURE_1D:
            glBindTexture(MYGL_TEXTURE_1D, tex_id)
            glUniform1i(self.mygl_internal_get_location("tex1D_" + str(number)), active_texture)
        elif tex_type == MYGL_TEXTURE_2D:
            glBindTexture(MYGL_TEXTURE_2D, tex_id)
            glUniform1i(self.mygl_internal_get_location("tex2D_" + str(number)), active_texture)
        elif tex_type == MYGL_TEXTURE_3D:
            glBindTexture(MYGL_TEXTURE_3D, tex_id)
            glUniform1i(self.mygl_internal_get_location("tex3D_" + str(number)), active_texture)
        elif tex_type == MYGL_TEXTURE_CUBE:
            glBindTexture(MYGL_TEXTURE_CUBE, tex_id)
            glUniform1i(self.mygl_internal_get_location("texCube_" + str(number)), active_texture)
        glActiveTexture(GL_TEXTURE0)

    def pass_float(self, name, float): glUniform1f(self.mygl_internal_get_location(name), float)

    def pass_int(self, name, int): glUniform1i(self.mygl_internal_get_location(name), int)

    def pass_bool(self, name, bool): glUniform1i(self.mygl_internal_get_location(name), bool)

    def pass_vec2(self, name, vec2): glUniform2f(self.mygl_internal_get_location(name), vec2[0], vec2[1])

    def pass_vec3(self, name, vec3): glUniform3f(self.mygl_internal_get_location(name), vec3[0], vec3[1], vec3[2])

    def pass_vec4(self, name, vec4): glUniform4f(self.mygl_internal_get_location(name), vec4[0], vec4[1], vec4[2],
                                                 vec4[3])

    def mygl_internal_format_matrix(self, mat): return np.array(mat, dtype="f")

    def pass_mat2(self, name, mat2): glUniformMatrix2fv(self.mygl_internal_get_location(name), 1, False, self.mygl_internal_format_matrix(mat2))

    def pass_mat3(self, name, mat3): glUniformMatrix3fv(self.mygl_internal_get_location(name), 1, False, self.mygl_internal_format_matrix(mat3))

    def pass_mat4(self, name, mat4): glUniformMatrix4fv(self.mygl_internal_get_location(name), 1, False, self.mygl_internal_format_matrix(mat4))

    def get_uniform(self, name, size):
        value = (GLfloat * size)()
        glGetUniformfv(self.program, self.locations[name], value)
        value = list(value)
        if size == 1: value = value[0]
        elif size == 9: value = np.array(value).reshape((3, 3))
        elif size == 16: value = np.array(value).reshape((4, 4))
        return value


class mygl_internal_ShaderSource:
    def __init__(self, version, renderequation, uservars, pre_vertex, verttrans, post_vertex, uvtrans, extfuncvert,
                 extfuncfrag, max2Dtextures, max3Dtextures, maxcubetextures, maxdeptheffects, maxrendertargets):
        # VERTEX SHADER SOURCE GENERATION
        self.vert = version
        self.vert += """varying vec3 vVertex;
        varying vec3 t;
        varying vec3 b;
        varying vec3 n;
        varying vec3 realnorm;\n"""
        if maxdeptheffects > 0:
            self.vert += """varying vec4 """ + "".join(
                "depth_coord_" + str(x) + "," for x in range(1, maxdeptheffects + 1, 1))[:-1] + ";"
        self.vert += """
        varying vec4 vertex;\n\n"""
        if max2Dtextures != 0: self.vert += "uniform sampler2D   " + "".join(
            "tex2D_" + str(x) + "," for x in range(1, max2Dtextures + 1, 1))[:-1] + ";\n"
        if max3Dtextures != 0: self.vert += "uniform sampler3D   " + "".join(
            "tex3D_" + str(x) + "," for x in range(1, max3Dtextures + 1, 1))[:-1] + ";\n"
        if maxcubetextures != 0: self.vert += "uniform samplerCube " + "".join(
            "texCube_" + str(x) + "," for x in range(1, maxcubetextures + 1, 1))[:-1] + ";\n"
        self.vert += """\nattribute vec4 vert_tangent;\n"""
        if maxdeptheffects > 0:
            self.vert += """uniform mat4 """ + "".join(
                "gllibinternal_depthmatrix_" + str(x) + "," for x in range(1, maxdeptheffects + 1, 1))[:-1] + """;"""
        self.vert += uservars
        self.vert += """

        float sum(vec2 vec) { return vec.x+vec.y; }
        float sum(vec3 vec) { return vec.x+vec.y+vec.z; }
        float sum(vec4 vec) { return vec.x+vec.y+vec.z+vec.w; }

        float round(float num) {
            float fnum = floor(num);
            return (num-fnum)>0.5 ? fnum+1.0:fnum;
        }
        vec2 round(vec2 num) { return vec2(round(num.x),round(num.y)); }
        vec3 round(vec3 num) { return vec3(round(num.x),round(num.y),round(num.z)); }
        vec4 round(vec4 num) { return vec4(round(num.x),round(num.y),round(num.z),round(num.w)); }
        """
        self.vert += mygl_internal_get_rotation_funcs(pre_vertex + verttrans + post_vertex)
        self.vert += extfuncvert
        self.vert += """
        void main() {
            gl_TexCoord[0] = gl_MultiTexCoord0;\n
            vertex = gl_Vertex;
            realnorm = gl_Normal;\n"""
        self.vert += pre_vertex
        self.vert += """
            n = gl_NormalMatrix*realnorm;
            t = normalize(gl_NormalMatrix*vert_tangent.xyz);
            b = cross(n,t)*vert_tangent.w;"""
        self.vert += verttrans
        self.vert += """\n
            vVertex = vec4(gl_ModelViewMatrix*vertex).xyz;"""
        for x in range(1, maxdeptheffects + 1, 1):
            self.vert += """
            depth_coord_""" + str(x) + """ = gllibinternal_depthmatrix_""" + str(x) + """*vertex;"""
        self.vert += """\n
            gl_Position = gl_ModelViewProjectionMatrix*vertex;"""
        self.vert += post_vertex
        self.vert += """
        }"""
        # FRAGMENT SHADER SOURCE GENERATION
        self.frag = version
        self.frag += """varying vec3 vVertex,t,b,n,realnorm;
        varying vec4 """ + "".join("depth_coord_" + str(x) + "," for x in range(1, maxdeptheffects + 1, 1))[:-1] + """;
        varying vec4 vertex;\n\n"""
        if max2Dtextures != 0: self.frag += "uniform sampler2D   " + "".join(
            "tex2D_" + str(x) + "," for x in range(1, max2Dtextures + 1, 1))[:-1] + ";\n"
        if max3Dtextures != 0: self.frag += "uniform sampler3D   " + "".join(
            "tex3D_" + str(x) + "," for x in range(1, max3Dtextures + 1, 1))[:-1] + ";\n"
        if maxcubetextures != 0: self.frag += "uniform samplerCube " + "".join(
            "texCube_" + str(x) + "," for x in range(1, maxcubetextures + 1, 1))[:-1] + ";\n"
        self.frag += """vec3 E,normal,L;
        uniform mat4 transform_matrix;"""
        self.frag += uservars
        ##        genType round(genType num) { return sign(num)*floor(abs(num)+0.5); }
        self.frag += """

        float sum(vec2 vec) { return vec.x+vec.y; }
        float sum(vec3 vec) { return vec.x+vec.y+vec.z; }
        float sum(vec4 vec) { return vec.x+vec.y+vec.z+vec.w; }
        float sum(mat2 mat) { return sum(mat[0])+sum(mat[1]); }
        float sum(mat3 mat) { return sum(mat[0])+sum(mat[1])+sum(mat[2]); }
        float sum(mat4 mat) { return sum(mat[0])+sum(mat[1])+sum(mat[2])+sum(mat[3]); }

        float round(float num) {
            float fnum = floor(num);
            return (num-fnum)>0.5 ? fnum+1.0:fnum;
        }
        vec2 round(vec2 num) { return vec2(round(num.x),round(num.y)); }
        vec3 round(vec3 num) { return vec3(round(num.x),round(num.y),round(num.z)); }
        vec4 round(vec4 num) { return vec4(round(num.x),round(num.y),round(num.z),round(num.w)); }

        float depth_from_depthbuffer(float depth,float near,float far) { return ((-(far*near)/((depth*(far-near))-far))-near)/(far-near); }

        vec3 normal_from_normalmap(vec3 normalmapsample) {
            vec3 tangent_norm = vec3((normalmapsample.rg-0.5)*2.0,normalmapsample.b);
            vec3 world_norm = normalize(mat3(t,b,n)*tangent_norm);
            return world_norm;
        }
        vec3 normal_from_normalmap(vec3 normalmapsample,float perturbation) {
            vec3 tangent_norm = vec3((normalmapsample.rg-0.5)*2.0*perturbation,normalmapsample.b);
            vec3 world_norm = normalize(mat3(t,b,n)*tangent_norm);
            return world_norm;
        }
        """
        self.frag += mygl_internal_get_parallaxmap_funcs(uvtrans)
        self.frag += mygl_internal_get_subsurface_absorb_funcs(renderequation)
        self.frag += mygl_internal_get_shadow_funcs(renderequation)
        self.frag += mygl_internal_get_cubemap_funcs(renderequation)
        self.frag += mygl_internal_get_light_funcs(renderequation)
        self.frag += mygl_internal_get_caustic_func(renderequation)
        rot = mygl_internal_get_rotation_funcs(renderequation)
        if rot == "":
            rot = mygl_internal_get_rotation_funcs(extfuncfrag)
        self.frag += rot
        self.frag += mygl_internal_get_texture_funcs(renderequation)

        self.frag += extfuncfrag

        self.frag += """
        void main() {
            vec4 color = vec4(0.0,0.0,0.0,1.0);
            """
        if "clamp_color" in renderequation:
            self.frag += "bool clamp_color = true;"
        if maxrendertargets > 1:
            for i in range(maxrendertargets):
                if "color" + str(i + 2) in renderequation:
                    self.frag += "vec4 color" + str(i + 2) + " = vec4(0.0,0.0,0.0,1.0);"
                    if "clamp_color" + str(i + 2) in renderequation:
                        self.frag += "bool clamp_color" + str(i + 2) + " = true;"
        self.frag += """
            E = normalize(-vVertex);
            vec2 uv  = gl_TexCoord[0].st;
            vec3 uvw = gl_TexCoord[0].stp;
            """
        self.frag += uvtrans
        if "_color" in renderequation:
            self.frag += """
            vec4 ambient_color =  gl_FrontMaterial.ambient;
            vec4 diffuse_color =  gl_FrontMaterial.diffuse;
            vec4 specular_color = gl_FrontMaterial.specular;
            vec4 emission_color = gl_FrontMaterial.emission;"""
        self.frag += """
            normal = normalize(n);"""
        for num in range(1, 9, 1):
            renderequation = renderequation.replace("light" + str(num), "gl_LightSource[" + str(num - 1) + "]")
        self.frag += """
            """ + renderequation
        self.frag += "\n"
        if "clamp_color" in renderequation:
            self.frag += "if (clamp_color) { color = clamp(color,0.0,1.0); }"
        else:
            self.frag += """
            color = clamp(color,0.0,1.0);"""
        self.frag += """
            gl_FragData[0] = color;"""
        if maxrendertargets > 1:
            for i in range(maxrendertargets):
                if "color" + str(i + 2) in renderequation:
                    if "clamp_color" + str(i + 2) in renderequation:
                        self.frag += "if (clamp_color" + str(i + 2) + ") { color" + str(i + 2) + " = clamp(color" + str(
                            i + 2) + ",0.0,1.0); }"
                    else:
                        self.frag += "color" + str(i + 2) + " = clamp(color" + str(i + 2) + ",0.0,1.0);"
                    self.frag += "gl_FragData[" + str(i + 1) + "] = color" + str(i + 2) + ";"
        self.frag += """
        }"""
